package tuding.android.bigplanettracks;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import tuding.android.bigplanettracks.imageupload.ImageUploaderActivity;
import tuding.android.bigplanettracks.maps.Marker;
import tuding.android.bigplanettracks.maps.MarkerManager;
import tuding.android.bigplanettracks.maps.PhysicMap;
import tuding.android.bigplanettracks.maps.Place;
import tuding.android.bigplanettracks.maps.RawTile;
import tuding.android.bigplanettracks.maps.SHA1Hash;
import tuding.android.bigplanettracks.maps.db.DAO;
import tuding.android.bigplanettracks.maps.db.GeoBookmark;
import tuding.android.bigplanettracks.maps.geoutils.GeoUtils;
import tuding.android.bigplanettracks.maps.loader.TileLoader;
import tuding.android.bigplanettracks.maps.providers.MapStrategyFactory;
import tuding.android.bigplanettracks.maps.storage.LocalStorageWrapper;
import tuding.android.bigplanettracks.maps.storage.SQLLocalStorage;
import tuding.android.bigplanettracks.maps.tools.MapSaverUI;
import tuding.android.bigplanettracks.maps.tuding.ActionBase;
import tuding.android.bigplanettracks.maps.tuding.ActionNotes;
import tuding.android.bigplanettracks.maps.tuding.AutoPause;
import tuding.android.bigplanettracks.maps.tuding.GMOT;
import tuding.android.bigplanettracks.maps.tuding.InfoWindow;
import tuding.android.bigplanettracks.maps.tuding.LocationEx;
import tuding.android.bigplanettracks.maps.ui.AddBookmarkDialog;
import tuding.android.bigplanettracks.maps.ui.AddNotesDialog;
import tuding.android.bigplanettracks.maps.ui.MapControl;
import tuding.android.bigplanettracks.maps.ui.OnDialogClickListener;
import tuding.android.bigplanettracks.maps.ui.OnMapLongClickListener;
import tuding.android.bigplanettracks.maps.ui.SmoothZoomEngine;
import tuding.android.bigplanettracks.tracks.MyTimeUtils;
import tuding.android.bigplanettracks.tracks.TrackStoringThread;
import tuding.android.bigplanettracks.tracks.TrackTabViewActivity;
import tuding.android.bigplanettracks.tracks.db.TrackDBAdapter;
import tuding.android.bigplanettracks.tracks.writer.GpxTrackWriterEx;
import tuding.android.bigplanettracks.tudingConnection.BaseConnection;
import tuding.android.bigplanettracks.tudingConnection.UploadLocation;
import tuding.android.bigplanettracks.util.Preferences;
import android.app.Activity;
import android.app.AlarmManager;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.PendingIntent;
import android.app.ProgressDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Point;
import android.graphics.Rect;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Proxy;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.text.util.Linkify;
import tuding.android.bigplanettracks.maps.Logex;
import android.view.Display;
import android.view.Gravity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SubMenu;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.view.View.OnClickListener;
import android.view.ViewGroup.LayoutParams;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.RelativeLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.RadioGroup.OnCheckedChangeListener;

public class BigPlanet extends Activity {

	private static final String BOOKMARK_DATA = "bookmark";
	private static int SEARCH_ZOOM = 2;
    public static String PRIVACY_SETTING = "privacy.data";
	private Toast textMessage;
	//public String identifier = null;
	public static float density;
	public static float mapMagnification = 1f;
	private static boolean isMapMagnificationChanged;

	public MapControl mapControl;

	private static MarkerManager mm;
	public static AutoPause autoPause;

	public static LocationManager locationManager;
	public static ConnectivityManager connectivityManager;
	public static LocationManager locationManagerforRecord;
	public static Location currentLocation = null;
	public static Location previousLocation;
	public static long currentTimeMillis = 0;
	public static Location currentLocationBeforeRecording;
	public static long recordingTime;
	public static long timeRecord;
	public static boolean SHOULD_TRY_AGAIN_FLAG = false;
	protected static String locationProvider = null;
	public static long currentTrackID = 0;
	
	private static boolean isFirstEntry = true;
	protected static boolean inHome = false;
	public static boolean isFollowMode = true; // default value is auto follow
	public static boolean isGPSTracking = false;  // default false
	public static boolean isAutoRecording = false; 
	public static boolean isGPSTrackSaved = false;  // default false
	public static boolean isMapInCenter = false;
	public static boolean isDBdrawclear = false; // default false for DB clear
	public static boolean autoDisplayDB = false;
	public static boolean autoDisplayDBforMarker = false;
	public static double autoDisplayDB_Lat = 0;
	public static double autoDisplayDB_Lon = 0;
	public static boolean clearYellowPersonMarker = false;
	public static boolean showBookmarks = false;
	public static boolean showSearchResults = false;
	public static int STORE_THRESHOLD = 10;
	public static int CURRENT_MAX_MARKER_NUM = 100; // it should bigger than STORE_THRESHOLD
	public static int SHOW_MAX_MARKER_NUM = 20; // del 20% 
	public static boolean OMIT_MARKERS_FLAG = false;
	public static boolean StoreInProgress = false;
	public static int fittingZoom = -2;
	public static boolean appendingFlag = false;
	public static boolean Paused_flag = false;
	public static int Orientation_flag = 1; // portrait
	public static boolean isOpenInfoWin = false;
	public static boolean isOpenStatsWin = false;
	public static boolean isAddedInfoWinToMapControl = false;
	public static int ActionPhotoRC = 1;
	//callback indicator for uploading loc with photo
	public static int ActionPhotoUploadingWithLoc = 2;
	public static boolean isPressAgain = false;
	//public static final String SERVER_ADDRESS = "http://www.tuding.org/";
	public static final String SERVER_ADDRESS = "http://42.120.10.242/";
	//public static final String SERVER_ADDRESS = "http://www.tudinger.org/uchome/";

	
	
	public static final int Footer_Toggle_Track = 1;
	public static final int Footer_Autofollow = 2;
	public static final int Footer_UploadLocation = 3;
	public static final int Footer_Add_notes = 4;
	public static final int Footer_Add_photo = 5;
	public static final int Footer_Toggle_Record = 6;
	
	public static final int REQUESTLOCATIONUPDATE = 1001;
	public static final int UPLOADLOCATIONFORRECORD = 1002;
	
	private boolean SDCARD_AVAILABLE = true;
	
	private MySearchIntentReceiver searchIntentReceiver;
	private MyUpdateScreenIntentReceiver updateScreenIntentReceiver;
	private SimpleReceiver simpleReceiver;
	public static String SearchAction = "tuding.android.bigplanettracks.INTENTS.GOTO";
	public static String UpdateScreenAction = "tuding.android.bigplanettracks.INTENTS.UpdateScreen";
	public static String SimpleRequestLocationUpdateAction = "tuding.android.bigplanettracks.INTENTS.SimpleRequestLocationUpdateAction";
	public static String SimpleCancelLocationUpdateAction = "tuding.android.bigplanettracks.INTENTS.SimpleCancelLocationUpdateAction";
	private static RelativeLayout mRefreshGPS;
	
	private static ImageView scaleImageView;
	private static ImageView cross;
	private Point myGPSOffset;
		
	public static TrackDBAdapter DBAdapter;
	private ProgressDialog myGPSDialog = null;
	private Handler mainThreadHandler; // used by TrackStoringThread
	public Handler connectionHandler; // userd by tudingConnection
	public Handler autoRecordHandler;
	protected static Handler locationHandler;
	public static Handler updateInfoWindowHandler;
	public static Handler updateLocationUpdateHandler;
	public static Handler footerHandler;
	protected static Handler titleHandler;
	private static boolean isInit = false;
	
	public static ProgressDialog loginDialog = null;
	
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
		density = getResources().getDisplayMetrics().density;
		Paused_flag = false;
		AutoPause.First_start_flag = true;
		autoPause = new AutoPause();
		
		//Log.i("FLOW", "onCreate");

		//Fix no.bug_61
		if(DBAdapter == null)DBAdapter = new TrackDBAdapter();
		else BigPlanet.DBAdapter.open();
		
		mainThreadHandler = new Handler() {
			public void handleMessage(Message msg) {
				switch (msg.what) {
					case TrackStoringThread.SUCCESS:
						Intent myIntent = new Intent();
						myIntent.setClass(BigPlanet.this, TrackTabViewActivity.class);
						startActivity(myIntent);
						break;
						
					case TrackStoringThread.FAIL:
						DBAdapter.deleteTrack(currentTrackID);
						Toast.makeText(
								BigPlanet.this,
								getString(R.string.fail)+"\n"+(String)msg.obj,
								Toast.LENGTH_LONG).show();
						break;
						
					case TrackStoringThread.DialogDismiss:
						myGPSDialog.dismiss();
						break;
				}
				//MarkerManager.savedTrackGwoClearG.clear();
				MarkerManager.IteratorG = 0;
				mainThreadHandler.removeMessages(msg.what);
			}};
		
		
		updateInfoWindowHandler = new Handler() {
			public void handleMessage(Message msg) {
				if(msg.what == 1) {
					if(isOpenStatsWin) {
						MapControl.infowin.UpdateStatsAccuracy(BigPlanet.this);
						MapControl.infowin.UpdateStatsSpeed(BigPlanet.this);
					}
				}
				updateInfoWindowHandler.removeMessages(msg.what);
			}
		};
		
		locationHandler = new Handler() {
			public void handleMessage(Message msg) { // for power cons, need diff pause or not
				Location location = (Location) msg.obj;
				//Log.i("TRACK", "locationHandler: --->Paused_flag is "+Paused_flag+" action is "+msg.what);
				switch (msg.what) { 
				case MethodGoToMyLocation:
					if(isOpenStatsWin) MapControl.infowin.UpdateStats(BigPlanet.this);
					goToMyLocation(location, PhysicMap.getZoomLevel());
					break;
				case MethodTrackMyLocation:
					if(isOpenStatsWin) MapControl.infowin.UpdateStats(BigPlanet.this);
					trackMyLocation(location, PhysicMap.getZoomLevel());
					break;
				case MethodAddMarker:
					if(isOpenStatsWin) MapControl.infowin.UpdateStats(BigPlanet.this);
					addMarker(location, PhysicMap.getZoomLevel());
					break;
				case MethodUpdateScreen:
					if(Paused_flag == false){
					    mapControl.updateScreen(0);
					    //Log.i("TRACK", "locationHandler: update screen");
					}
					break;
				}
				locationHandler.removeMessages(msg.what);
			}
		};
		
		updateLocationUpdateHandler = new Handler() {
			public void handleMessage(Message msg) {
				//Log.i("GPS", "---->Update locaiont update");
				switch(msg.what) {
				case 1:
				    if(locationManager != null && gpsLocationListener != null) {
				    	try {
				    		//Log.i("AP", "AutoPause.PauseGear: "+AutoPause.PauseGear);
				    		//if(AutoPause.PauseGear>=3)showRefreshGPS(true);
				    		//locationManager.removeUpdates(gpsLocationListener);
				    		locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, AutoPause.PauseInterval[AutoPause.PauseGear], 0, gpsLocationListener);
				    	} catch (RuntimeException e) {
							e.printStackTrace();
						}
				    }
					break;
				
				case 2:
					//TODO: need test
					Logex.message("REMOVING","registering new location update request");
					int timeIntv = msg.arg1;
					if(locationManager != null && gpsLocationListener != null) {
						try {
							//locationManager.removeUpdates(gpsLocationListener);
							locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, timeIntv, 0, gpsLocationListener);
						} catch (RuntimeException e) {
							e.printStackTrace();
						}
				    }
					//Log.d("GPS", " changed Location Update Interval to "+ timeIntv);
					break;
				case 3:
					//TODO : 
					Logex.message("Recording","to upload to tudinger");
					SHOULD_TRY_AGAIN_FLAG = true;
					UploadLocation ul = new UploadLocation(BigPlanet.this, autoRecordHandler);
					ul.uploadLocationSimple(clocationRecord);
					break;
				case 4:
					showRefreshGPS(true);
					break;
				}
				// to clean message queue
				updateLocationUpdateHandler.removeMessages(msg.what);
				
			}
		};
		
		
		
		
		titleHandler = new Handler() {
			public void handleMessage(Message msg) {
				String title = getTitle(BigPlanet.locationProvider);
				BigPlanet.this.setTitle(title);
			}
		};
		
		footerHandler = new Handler() {
			public void handleMessage(Message msg) {
				switch(msg.what) {
				case Footer_Toggle_Track:
					toggleTrackButton();
					break;
				case Footer_Autofollow:
					enabledAutoFollow(BigPlanet.this);
					break;
				case Footer_UploadLocation:
					//uploadCurrentLocaltion();
					//Log.i("UPLOAD","upload from toolbar");
					break;
				case Footer_Toggle_Record:
					autoRecording();
					//Log.i("UPLOAD","upload from toolbar");
					break;
				case Footer_Add_notes:
					if(isGPSTracking && MarkerManager.markersG.size() > 2) addActionNotesHandler();
        	    	else if(!isGPSTracking)
        	    		Toast.makeText(
    						BigPlanet.this,
    						getString(R.string.FUNCTION_UNAVAILABLE_WO_TRACKING),
    						Toast.LENGTH_SHORT).show();
        	    	else Toast.makeText(
    						BigPlanet.this,
    						getString(R.string.WAIT_FOR_GPS_READYNESS),
    						Toast.LENGTH_SHORT).show();
					break;
				case Footer_Add_photo:
					if(isGPSTracking && MarkerManager.markersG.size() > 2) addActionPhotoHandler();
        	    	else if(!isGPSTracking)
        	    		Toast.makeText(
    						BigPlanet.this,
    						getString(R.string.FUNCTION_UNAVAILABLE_WO_TRACKING),
    						Toast.LENGTH_SHORT).show();
        	    	else Toast.makeText(
    						BigPlanet.this,
    						getString(R.string.WAIT_FOR_GPS_READYNESS),
    						Toast.LENGTH_SHORT).show();
					break;
				
				}
				footerHandler.removeMessages(msg.what);
			}
		};
		

		String status = Environment.getExternalStorageState();
		//Log.d("SDCARD", ""+status);
		if (!status.equals(Environment.MEDIA_MOUNTED)) {
			SDCARD_AVAILABLE = false;
			new AlertDialog.Builder(this).setMessage(R.string.sdcard_unavailable)
					.setCancelable(false).setNeutralButton(R.string.OK_LABEL,
							new DialogInterface.OnClickListener() {
								public void onClick(DialogInterface arg0,
										int arg1) {
									finish();
								}
							}).show();
		} else { 
			SDCARD_AVAILABLE = true;
			initRefreshGPSLayout();
			if (new File(SQLLocalStorage.TRACK_PATH+"/sdcard.xml").exists()) {
				SQLLocalStorage.SD_PATH = Environment.getExternalStorageDirectory().getAbsolutePath()+File.separator;
				SQLLocalStorage.updateSDPaths();
			}

			searchIntentReceiver = new MySearchIntentReceiver();
			registerReceiver(searchIntentReceiver, new IntentFilter(SearchAction));
			
			updateScreenIntentReceiver = new MyUpdateScreenIntentReceiver();
			registerReceiver(updateScreenIntentReceiver, new IntentFilter(UpdateScreenAction));
			
			locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
			locationManager.addGpsStatusListener(autoPause.gpsListener);
			
			connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
			
			simpleLocationService = new SimpleLocationService();
			locationManagerforRecord = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
			recordinganduploadingHandler = new Handler() {
				
				public void handleMessage(Message msg) {
					switch (msg.what)
					{
						case REQUESTLOCATIONUPDATE:
							if(simpleLocationService != null) {
								locationManagerforRecord.removeUpdates(simpleLocationService);
							}
							locationManagerforRecord.requestLocationUpdates(default_provider, 3000, 0, simpleLocationService); // need remove such update in timertask or locationchange
							break;
					}
					recordinganduploadingHandler.removeMessages(msg.what);
				}
				
			};
			
			// for autoRecord stuff
			registerAlarm = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
			cancelAlarm = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
			simpleReceiver = new SimpleReceiver();
			registerReceiver(simpleReceiver, new IntentFilter(SimpleRequestLocationUpdateAction));
			Intent registerIntent = new Intent(BigPlanet.SimpleRequestLocationUpdateAction);
			registerPendingIntent = PendingIntent.getBroadcast(this, 0, 
					registerIntent, PendingIntent.FLAG_UPDATE_CURRENT);
			
			registerReceiver(simpleReceiver, new IntentFilter(SimpleCancelLocationUpdateAction));
			Intent cancelIntent = new Intent(BigPlanet.SimpleCancelLocationUpdateAction);
			cancelPendingIntent = PendingIntent.getBroadcast(this, 0, 
					cancelIntent, PendingIntent.FLAG_UPDATE_CURRENT);
			
			SmoothZoomEngine.stop = false;
		

			
			
			File trackImportFolder = new File(SQLLocalStorage.TRACK_IMPORT_PATH);
			if (!trackImportFolder.exists())
				trackImportFolder.mkdirs();
			trackImportFolder = null;
			
			File mapsDBFolder = new File(SQLLocalStorage.MAP_PATH);
			if (!mapsDBFolder.exists())
				mapsDBFolder.mkdirs();
			mapsDBFolder = null;
			
			// import the db from res/raw
			init_offset_db(this);
 
			String proxyHost = Proxy.getDefaultHost();
			int proxyPort = Proxy.getDefaultPort();
			if (proxyHost != null && proxyPort != -1) {
				System.setProperty("http.proxyHost", proxyHost);
				System.setProperty("http.proxyPort", Integer.toString(proxyPort));
				//Log.i("Proxy", proxyHost+":"+proxyPort);
			}
			
			initializeMap();
			
			/* Create an ImageView with a auto-follow icon. */
			//mapControl.addView(mAutoFollowRelativeLayout); // We can just run it once.
			/* Create an ImageView with a Track icon. */
			//mapControl.addView(mTrackRelativeLayout); // We can just run it once.
			/* Create an ImageView with a scale image. */
			mapControl.addView(mRefreshGPS);
			//showRefreshGPS(true);
			
			scaleImageView = new ImageView(this);
			scaleImageView.setImageResource(R.drawable.scale1);
			mapControl.addView(scaleImageView);
			/* create cross in the middle of screen */
			cross = new ImageView(this);
			cross.setImageResource(R.drawable.cross);
			final RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
					RelativeLayout.LayoutParams.WRAP_CONTENT,
					RelativeLayout.LayoutParams.WRAP_CONTENT);
			params.addRule(RelativeLayout.CENTER_IN_PARENT);
			mapControl.addView(cross, params);
			
			/*			
			if (!Utils.verify(identifier)) {
				showTrialDialog(R.string.this_is_demo_title, R.string.this_is_demo_message);
			}*/
			setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);

			myGPSOffset = Preferences.getGPSOffset();
			setActivityTitle(BigPlanet.this);
			/*
			new Thread("LoadDefaultTimeZone") {
				public void run() {
					// pre-invoke to speedup for SDK 1.5
					String tz = "TimeZone="+TimeZone.getDefault().getID();
					System.out.println(tz+" ("+MyTimeUtils.getLocalTimeString(System.currentTimeMillis())+")");
				}
			}.start();*/
		}
		
		autoRecordHandler = new Handler() {
			public void handleMessage(Message msg) {
				switch (msg.what)
				{
					case UploadLocation.UPLOAD_INTERRUPTED:
						Toast.makeText(BigPlanet.this, BigPlanet.this.getString(R.string.UPLOAD_INTERRUPTED),	Toast.LENGTH_LONG).show();
						break;
					case UploadLocation.UPLOAD_DONE_WITH_SUCCESS:
						Toast.makeText(BigPlanet.this, BigPlanet.this.getString(R.string.SUBMIT_SUCCESS),	Toast.LENGTH_LONG).show();
						//Log.i("Recording", "---> sumbit success");
						Logex.message("Recording", "---> sumbit success");
						timeRecord = System.currentTimeMillis();
						Place place = new Place();
						place.setLat(clocationRecord.getLatitude());
						place.setLon(clocationRecord.getLongitude());
						place.setLocation(clocationRecord);
						MarkerManager.addMarker(place, PhysicMap.getZoomLevel(), MarkerManager.DrawMarkerForRecord,  MarkerManager.RECORDED_MARKER);
						mapControl.invalidate();
						break;
					case GpxTrackWriterEx.SOCKETEXCEPTION:
						//Toast.makeText(BigPlanet.this, getString(R.string.socket_exception), Toast.LENGTH_SHORT).show();
						Logex.message("UPLOAD", BigPlanet.this.getString(R.string.socket_exception));
						// try again TODO:
						if(SHOULD_TRY_AGAIN_FLAG){
							SHOULD_TRY_AGAIN_FLAG = false;
							UploadLocation ul = new UploadLocation(BigPlanet.this, autoRecordHandler);
							ul.uploadLocationSimple(clocationRecord);
						}
						break;
					case GpxTrackWriterEx.UPLOAD_DATA_TIMEOUT:
						//Toast.makeText(BigPlanet.this, getString(R.string.upload_data_timeout), Toast.LENGTH_SHORT).show();
						Logex.message("UPLOAD", BigPlanet.this.getString(R.string.upload_data_timeout));
						break;
					default:
						Toast.makeText(BigPlanet.this, BigPlanet.this.getString(R.string.unknown_error)+" !",	Toast.LENGTH_LONG).show();
						break;
				}
				autoRecordHandler.removeMessages(msg.what);
			}
		};
		//autoRecordHandler.removeMessages(0);
		
		connectionHandler = new Handler() {
			
			public void handleMessage(Message msg) {
				switch (msg.what)
				{
					case BaseConnection.LOGIN_SUCCESS:
						/* Export GpxFile Successfully*/
						Toast.makeText(BigPlanet.this, getString(R.string.login_success), 
								Toast.LENGTH_SHORT).show();
						
						Preferences.setLogined(true);
						break;
					case BaseConnection.LOGIN_FAILED:
						/* Export GpxFile Successfully*/
						Toast.makeText(BigPlanet.this, getString(R.string.login_failed), 
								Toast.LENGTH_SHORT).show();
						
						Preferences.setLogined(false);
						break;
					case BaseConnection.UNKNOWN_ERROR:
						/* Export GpxFile Successfully*/
						Toast.makeText(BigPlanet.this, getString(R.string.unknown_error), 
								Toast.LENGTH_SHORT).show();
						Logex.message("UPLOAD", getString(R.string.unknown_error));
						Preferences.setLogined(false);
						break;
					
					// for UploadLocation use
					case UploadLocation.NEED_LOGIN:
						Toast.makeText(BigPlanet.this, BigPlanet.this.getString(R.string.NEED_LOGIN),	Toast.LENGTH_LONG).show();
						Logex.message("UPLOAD", BigPlanet.this.getString(R.string.NEED_LOGIN));
						break;
					case UploadLocation.UPLOAD_INTERRUPTED:
						Toast.makeText(BigPlanet.this, BigPlanet.this.getString(R.string.UPLOAD_INTERRUPTED),	Toast.LENGTH_LONG).show();
						Logex.message("UPLOAD", BigPlanet.this.getString(R.string.UPLOAD_INTERRUPTED));
						break;
					case UploadLocation.UNKNOWN_ERROR:
						Toast.makeText(BigPlanet.this, BigPlanet.this.getString(R.string.unknown_error)+" !",	Toast.LENGTH_LONG).show();
						Logex.message("UPLOAD", BigPlanet.this.getString(R.string.unknown_error));
						break;
					case UploadLocation.UPLOAD_DONE_WITH_SUCCESS:
						Toast.makeText(BigPlanet.this, BigPlanet.this.getString(R.string.SUBMIT_SUCCESS),	Toast.LENGTH_LONG).show();
						Logex.message("UPLOAD", BigPlanet.this.getString(R.string.SUBMIT_SUCCESS));
						break;
					case GpxTrackWriterEx.SOCKETEXCEPTION:
						Toast.makeText(BigPlanet.this, getString(R.string.socket_exception), Toast.LENGTH_SHORT).show();
						Logex.message("UPLOAD", BigPlanet.this.getString(R.string.socket_exception));
						break;
					case GpxTrackWriterEx.UPLOAD_DATA_TIMEOUT:
						Toast.makeText(BigPlanet.this, getString(R.string.upload_data_timeout), Toast.LENGTH_SHORT).show();
						Logex.message("UPLOAD", BigPlanet.this.getString(R.string.upload_data_timeout));
						break;
					default:
						Toast.makeText(BigPlanet.this, BigPlanet.this.getString(R.string.unknown_error),	Toast.LENGTH_LONG).show();
						break;
					}
				connectionHandler.removeMessages(msg.what);
				}
		};
		//connectionHandler.removeMessages(0);
	}
	
	public void disabledAutoFollow(Context context) {
		if (isFollowMode) {
			Toast.makeText(context, R.string.auto_follow_disabled, Toast.LENGTH_SHORT).show();
			//mAutoFollowRelativeLayout.setVisibility(View.VISIBLE);
			isFollowMode = false;
			setActivityTitle((Activity) context);
		}
	}
	
	public void enabledAutoFollow(Context context) {
		if (!isFollowMode) { 
			Toast.makeText(context, R.string.auto_follow_enabled, Toast.LENGTH_SHORT).show();
			//if (mAutoFollowRelativeLayout == null) {
			//	mAutoFollowRelativeLayout = getAutoFollowRelativeLayout();
			//}
			//mAutoFollowRelativeLayout.setVisibility(View.INVISIBLE);
			
			if (currentLocation != null)
				goToMyLocation(currentLocation, PhysicMap.getZoomLevel());
			isFollowMode = true;
		}
		if(isGPSTracking && !isOpenStatsWin) {
			MapControl.infowin = new InfoWindow(context, this.mapControl);
			isOpenStatsWin = true;
		}
		setActivityTitle((Activity) context);
	}
	
	

	
	private void enabledTrack(Context context) {
		if (isGPSTracking) {
			MarkerManager.savedTrackG.clear();
			MarkerManager.IteratorG = 0;
			//MarkerManager.savedTrackGwoClearG.clear();
			Toast.makeText(context, R.string.track_enabled, Toast.LENGTH_SHORT).show();
			// start service
			Intent intent = new Intent(this, MyLocationService.class);
			this.startService(intent);
		}
		setActivityTitle((Activity) context);
		mapControl.invalidate();
	}
	
	private void disabledTrack(Context context) {
		if (!isGPSTracking) {
			// stop service
			Intent intent = new Intent(this, MyLocationService.class);
			this.stopService(intent);
		}
		mm.saveMarkerGTrack();
		isGPSTrackSaved = true;
		setActivityTitle((Activity) context);
		mapControl.invalidate();
	}




	/**
	 * get photos from gallery or from camera
	 */
	private void addActionPhotoHandler() {
	    final ActionBase newActionBase = new ActionBase();
        tuding.android.bigplanettracks.maps.geoutils.Point point_tmp2 = GeoUtils.toTileXY(currentLocation.getLatitude(), currentLocation.getLongitude(), PhysicMap.getZoomLevel());
        tuding.android.bigplanettracks.maps.geoutils.Point point_tmp1 = GeoUtils.getPixelOffsetInTile(currentLocation.getLatitude(), currentLocation.getLongitude(), PhysicMap.getZoomLevel());
        
        newActionBase.setOffsetX((int)point_tmp1.x);
        newActionBase.setOffsetY((int)point_tmp1.y);
        // measureVersion=2 means need update TRACK_TABLE when view
		DBAdapter.updateTrack(currentTrackID, 2);
        newActionBase.setTile(new RawTile((int)point_tmp2.x, (int)point_tmp2.y, mapControl.getPhysicalMap().getDefaultTile().z, mapControl.getPhysicalMap().getDefaultTile().s));
        newActionBase.getTile().s = mapControl.getPhysicalMap().getTileResolver().getMapSourceId();
        Intent intent = new Intent(this,ImageUploaderActivity.class);
        this.startActivityForResult(intent, ActionPhotoRC);
	   
	}
	
	private void addActionNotesHandler() {
		final ActionBase newActionBase = new ActionBase();
    	tuding.android.bigplanettracks.maps.geoutils.Point point_tmp2 = GeoUtils.toTileXY(currentLocation.getLatitude(), currentLocation.getLongitude(), PhysicMap.getZoomLevel());
    	tuding.android.bigplanettracks.maps.geoutils.Point point_tmp1 = GeoUtils.getPixelOffsetInTile(currentLocation.getLatitude(), currentLocation.getLongitude(), PhysicMap.getZoomLevel());
    	
		newActionBase.setOffsetX((int)point_tmp1.x);
		newActionBase.setOffsetY((int)point_tmp1.y);
		newActionBase.setLat((float)currentLocation.getLatitude());
		newActionBase.setLon((float)currentLocation.getLongitude());
		newActionBase.setTile(new RawTile((int)point_tmp2.x, (int)point_tmp2.y, mapControl.getPhysicalMap().getDefaultTile().z, mapControl.getPhysicalMap().getDefaultTile().s));
		newActionBase.getTile().s = mapControl.getPhysicalMap().getTileResolver().getMapSourceId();
		newActionBase.setDate(System.currentTimeMillis());
		newActionBase.setSpeed((int)currentLocation.getSpeed());
		newActionBase.setAltitude((int)currentLocation.getAltitude());
		newActionBase.setTrackID((int)currentTrackID);
		// measureVersion=2 means need update TRACK_TABLE when view
		DBAdapter.updateTrack(currentTrackID, 2);
		
    	AddNotesDialog.show(BigPlanet.this, newActionBase, new OnDialogClickListener() {
			@Override
			public void onCancelClick() {
			}
			 
			@Override
			public  void onOkClick(Object obj) {
				ActionBase newActionBase = (ActionBase) obj;
				ActionNotes d = new ActionNotes(BigPlanet.this);
				long actionID = d.saveActionNotes(newActionBase);
				d.close();
				//Log.i("MARKER", "New action id is "+actionID);
				Place place = new Place();
				place.setLat(currentLocation.getLatitude());
				place.setLon(currentLocation.getLongitude());
				place.setLocation(currentLocation);
				MarkerManager.addMarker(place, PhysicMap.getZoomLevel(), MarkerManager.DrawMarkerOrTrack, MarkerManager.ACTION_NOTES_MARKER, 1, actionID);
				mapControl.invalidate();
			}
		});
	}
	
    synchronized public static void init_offset_db(Context context)
    {
        if(isInit) 
            return;
        String outFileName = SQLLocalStorage.OFFSET_PATH + GMOT.DATABASE_NAME;

        File dir = new File(outFileName);
        if(dir.exists())
            return;

        dir = new File(SQLLocalStorage.OFFSET_PATH);

        if (!dir.exists())
            dir.mkdir();
        dir = null;

        InputStream input = null;
        OutputStream output = null;
        // 从资源中读取数据库流
        input = context.getResources().openRawResource(R.raw.gmot);

        try {
            output = new FileOutputStream(outFileName);

            // 拷贝到输出流
            byte[] buffer = new byte[2048];
            int length;
            while ((length = input.read(buffer)) > 0) {
                output.write(buffer, 0, length);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
        	try {
                output.flush();
                output.close();
            } catch (IOException e) {
            }
            try {
                input.close();
            } catch (IOException e) {
            }
        }
        isInit = true;
    }
	
	private void toggleTrackButton() {
		if (!isGPSTracking) {
			// a dialog to make sure that user wants to start recording
			new AlertDialog.Builder(BigPlanet.this).setTitle(R.string.start_tracking)
			.setMessage(R.string.ACTION_TRACKING_INTRO)
			
			.setPositiveButton(
					R.string.ACTION_NEW,
					new DialogInterface.OnClickListener() {
						public void onClick(DialogInterface dialog,
								int whichButton) {
							startTracking();
						}
					})
			.setNeutralButton(
					R.string.ACTION_APPEND,
					new DialogInterface.OnClickListener() {
						public void onClick(DialogInterface dialog,
								int whichButton) {
							appendTracking();
						}
					})
			.setNegativeButton(
					R.string.ACTION_CANCEL,
					new DialogInterface.OnClickListener() {
				        public void onClick(DialogInterface dialog,
						        int whichButton) {
				        }
			        })
			.show();
		} else {
			if (MarkerManager.getLocationList(MarkerManager.markersG).size()>1) {
				// a dialog to make sure that user wants to finish recording
				new AlertDialog.Builder(BigPlanet.this).setTitle(R.string.finish_tracking)
				.setPositiveButton(
						R.string.YES_LABEL,
						new DialogInterface.OnClickListener() {
							public void onClick(DialogInterface dialog,
									int whichButton) {
								finishTracking();
							}
						})
				.setNeutralButton(
						R.string.NO_LABEL,
						new DialogInterface.OnClickListener() {
							public void onClick(DialogInterface dialog,
									int whichButton) {
							}
						})
				.show();
			} else {
				finishTracking();
			}
		}
	}
	
	private void toggle_record_btn(int On_Off){
		if(mapControl != null) {
			int old_R, new_R;
			if(1 == On_Off){ 
				old_R = R.drawable.stop_track32;
				new_R = R.drawable.start_track32;
			} else {
				old_R = R.drawable.start_track32;
				new_R = R.drawable.stop_track32;
			}
			//Log.i("FLOW", "toggle_record_btn");
			LinearLayout llv = (LinearLayout)mapControl.findViewById(old_R);
			if(llv == null) llv = (LinearLayout)mapControl.findViewById(new_R);
			try {
				Button btn = (Button) llv.findViewById(R.id.launchbar_button_item);
				btn.setCompoundDrawablesWithIntrinsicBounds(0, new_R, 0, 0);
			}catch(Exception e){
				e.printStackTrace();
			}
		}
	}
	
	
	private RelativeLayout getRefreshGPSLayout() {
		final RelativeLayout relativeLayout = new RelativeLayout(this);
		
		/* Create an ImageView with a auto-follow icon. */
		final ImageView needRefreshGPS = new ImageView(this);
		needRefreshGPS.setImageResource(R.drawable.refresh);
		needRefreshGPS.setOnClickListener(new OnClickListener(){
			@Override
			public void onClick(View v) {
				enabledGPS();
			}
		});
		
		/* Create RelativeLayoutParams, that position in in the bottom right corner. */
		final RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
				RelativeLayout.LayoutParams.WRAP_CONTENT,
				RelativeLayout.LayoutParams.WRAP_CONTENT);
		params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
		params.addRule(RelativeLayout.ALIGN_PARENT_TOP);
		relativeLayout.addView(needRefreshGPS, params);
		
		return relativeLayout;
	}
	
	private void initRefreshGPSLayout() {
		mRefreshGPS = getRefreshGPSLayout();
		mRefreshGPS.setVisibility(View.INVISIBLE);
	}
	//TODO:
	public static void showRefreshGPS(boolean bEnable) {
		if(bEnable) {
			mRefreshGPS.setVisibility(View.VISIBLE);
		}
		else mRefreshGPS.setVisibility(View.INVISIBLE);
	}
	
	private void enabledGPS() {
		if (locationManager != null) {
			if(!locationManager.isProviderEnabled(android.location.LocationManager.GPS_PROVIDER)) {
				Toast.makeText(BigPlanet.this, getString(R.string.NEEDENABLEGPS), 
						Toast.LENGTH_SHORT).show();
			}
			else if(autoPause!=null) autoPause.AP_sleep_lighter();
		}
	}
	
	
	private void appendTracking() {
		isGPSTracking = true;
		appendingFlag = true;
		AutoPause.PauseGear = 0;
		autoPause.AP_sleep_lighter();
		toggle_record_btn(0);
		enabledTrack(BigPlanet.this);
		currentLocationBeforeRecording = currentLocation;
		//recordingTime = System.currentTimeMillis();
		DBAdapter.open();
		//currentTrackID = DBAdapter.getLatestTrack();
		currentTrackID = DBAdapter.getLatestTrackID();
		if(currentTrackID == 0) {
			Toast.makeText(
					BigPlanet.this,
					getString(R.string.no_previous_track),
					Toast.LENGTH_SHORT).show();
			return;
		}
		recordingTime = DBAdapter.getTrack(currentTrackID).getLong(3); // FIELD_startTime
		// clear all loaded tracks from screen, then load the latest one to markesG 
		MarkerManager.savedTrackG.clear();
		clearMarkerDB();
		ArrayList<LocationEx> locationList = TrackTabViewActivity.getLocationListFromDBEx(currentTrackID);
		ArrayList<Place> placeList = TrackTabViewActivity.convertToPlaceListEx(locationList);
		//Log.d("LIST", "placelist size is "+placeList.size());
		for(int i=0; i<placeList.size(); i++) {
			Place place = placeList.get(i);
			Marker marker = new Marker(placeList.get(i), MarkerManager.images.get(MarkerManager.MY_LOCATION_MARKER), true);
			if(place.getActionType() != 0 && place.getActionID() != 0) {
				//Log.i("LL","addMarkersForDrawing type is: "+place.getActionType());
				int imageType = MarkerManager.MY_LOCATION_MARKER;
				if(place.getActionType() == 1) imageType = MarkerManager.ACTION_NOTES_MARKER;
				else if(place.getActionType() == 2) imageType = MarkerManager.ACTION_PHOTO_MARKER;
				marker = new Marker(placeList.get(i), MarkerManager.images.get(imageType), true, imageType, place.getActionType(), place.getActionID());
			} 
			MarkerManager.updateParams(marker, PhysicMap.getZoomLevel());
			MarkerManager.markersG.add(marker);
		}
		MarkerManager.IteratorG = placeList.size();
		goToMyLocation(currentLocation, PhysicMap.getZoomLevel());
		//Log.i("TRACK", "current ID is "+currentTrackID);
		if (locationManager != null) {
			if(!locationManager.isProviderEnabled(android.location.LocationManager.GPS_PROVIDER)) {
				Toast.makeText(BigPlanet.this, getString(R.string.NEEDENABLEGPS), 
						Toast.LENGTH_LONG).show();
			}
			
			if (networkLocationListener != null) {
				locationManager.removeUpdates(networkLocationListener);
			}
			BigPlanet.locationProvider = "gps 1 0";
			setActivityTitle(BigPlanet.this);
		}
	}
	
	private void startTracking() {
		isGPSTracking = true;
		AutoPause.PauseGear = 0;
		autoPause.AP_sleep_lighter();
		toggle_record_btn(0);
		enabledTrack(BigPlanet.this);
		currentLocationBeforeRecording = currentLocation;
		recordingTime = System.currentTimeMillis();
		DBAdapter.open();
		currentTrackID = DBAdapter.insertNewTrack("", "", MyTimeUtils.getGMTTimeString(recordingTime), "GPS");
		//Log.i("TRACK", "new track id is "+currentTrackID);
		// log track by using gpsLocationListener
		if (locationManager != null) {
			if(!locationManager.isProviderEnabled(android.location.LocationManager.GPS_PROVIDER)) {
				Toast.makeText(BigPlanet.this, getString(R.string.NEEDENABLEGPS), 
						Toast.LENGTH_LONG).show();
			}
			
			if (networkLocationListener != null) {
				locationManager.removeUpdates(networkLocationListener);
			} 
			BigPlanet.locationProvider = "gps 1 0";
			setActivityTitle(BigPlanet.this);
		}

	}

	private void finishTracking() {
		isGPSTracking = false;
		Logex.message("MARKER","Finishing Tracking: savedTrackG size: "+(MarkerManager.savedTrackG).size()+" markersG:"+MarkerManager.markersG.size());
		toggle_record_btn(1);
		disabledTrack(BigPlanet.this);
		InfoWindow.resetSTATS();
		Logex.message("MARKER"," Now                savedTrackG size: "+(MarkerManager.savedTrackG).size()+" markersG:"+MarkerManager.markersG.size());
//		Log.i("Message","Start to store GPS Locations to DB...");
	
		// check out whether GPS LocationList contains any GPS data or not
		// due to at least two locations needed to compute the "Distance" measurement
		// there are still markers did not store on the fly:
		Logex.message("MARKER","savedTrackG size: "+(MarkerManager.savedTrackG).size()+" IteratorG:"+MarkerManager.IteratorG);
		//if ((MarkerManager.getLocationList(MarkerManager.savedTrackG).size()-MarkerManager.IteratorG)>0) {
		int tmp_count = MarkerManager.getLocationList(MarkerManager.savedTrackG).size();
		//int tmp_count = MarkerManager.savedTrackG.size();
		tmp_count += MarkerManager.markersG.size();
		Logex.message("MARKER", "tmp_count is "+tmp_count);
		if(tmp_count > 1) {
			final CharSequence strDialogTitle = getString(R.string.str_store_gps_location_to_db_title);
			final CharSequence strDialogBody = getString(R.string.str_store_gps_location_to_db_body);
			myGPSDialog = ProgressDialog.show(
					BigPlanet.this,
					strDialogTitle,
					strDialogBody, 
					true);
			
			mm.storeMarkersG_block();
			BigPlanet.DBAdapter.updateTrack(BigPlanet.currentTrackID, 2); // 2 means need update stat : measureVersion
			// update all information
			TrackStoringThread.TrackID = currentTrackID;
			TrackStoringThread trackStoringThread = new TrackStoringThread();
			trackStoringThread.setMainHandler(mainThreadHandler);
			trackStoringThread.setLocationListfromDB(currentTrackID);
			trackStoringThread.start();
		} else {
			Toast.makeText(
						BigPlanet.this,
						getString(R.string.gps_locationlist_has_no_data),
						Toast.LENGTH_LONG).show();
			clearSaveTracksG();
			DBAdapter.open();
			DBAdapter.deleteTrack(currentTrackID);
//			Log.i("Message","The size of LocationList is less than two points");
		}
	}

	private void initializeMap() {
		// create maps
		mm = new MarkerManager(getResources());
		RawTile savedTile = Preferences.getTile();
		//Log.i("TILE","initializeMap deftile: "+savedTile.x+" "+savedTile.y+" "+savedTile.z );
		//savedTile.s = 0;
		configMapControl(savedTile);
		// use the network or not
		boolean useNet = Preferences.getUseNet();
		mapControl.getPhysicalMap().getTileResolver().setUseNet(useNet);
		// map source
		int mapSourceId = Preferences.getSourceId();
		mapControl.getPhysicalMap().getTileResolver().setMapSource(mapSourceId);
		mapControl.getPhysicalMap().getDefaultTile().s = mapSourceId;
		// global offset 
		Point globalOffset = Preferences.getOffset();
		//Log.i("PREF", "offset is :"+globalOffset.x+" "+globalOffset.y);
		//globalOffset.x = 0;
		//globalOffset.y = -32;
//		System.out.println("offset " + globalOffset + " " + savedTile);
		mapControl.getPhysicalMap().setGlobalOffset(globalOffset);
		mapControl.getPhysicalMap().reloadTiles();
	}

	public class MySearchIntentReceiver extends BroadcastReceiver {
		/**
		 * @see adroid.content.BroadcastReceiver#onReceive(android.content.Context,
		 *      android.content.Intent)
		 */
		@Override
		public void onReceive(Context context, Intent intent) {
			isFollowMode = true;
			disabledAutoFollow(BigPlanet.this);
			int z = SEARCH_ZOOM;
			Place place = (Place) intent.getSerializableExtra("place");
			showBookmarks = false;
			mm.clearMarkerManager();
			MarkerManager.addMarker(place, z, MarkerManager.DrawMarkerForSearch, MarkerManager.SEARCH_MARKER);
			tuding.android.bigplanettracks.maps.geoutils.Point p = GeoUtils.toTileXY(place.getLat(), place.getLon(), z);
			tuding.android.bigplanettracks.maps.geoutils.Point off = GeoUtils.getPixelOffsetInTile(place.getLat(), place.getLon(), z);
			mapControl.goTo((int) p.x, (int) p.y, z, (int) off.x, (int) off.y);
			showSearchResults = true;
			
			//final GeoBookmark newGeoBookmark = new GeoBookmark();
			//String query = (String) intent.getSerializableExtra("query");
			//newGeoBookmark.setName(query);
			//newGeoBookmark.setOffsetX(mapControl.getPhysicalMap().getGlobalOffset().x);
			//newGeoBookmark.setOffsetY(mapControl.getPhysicalMap().getGlobalOffset().y);
			//newGeoBookmark.setTile(mapControl.getPhysicalMap().getDefaultTile());
			//newGeoBookmark.getTile().s = mapControl.getPhysicalMap().getTileResolver().getMapSourceId();
			//DAO d = new DAO(BigPlanet.this);
			//d.saveGeoBookmark(newGeoBookmark);
		}
	}
	
	public static final int MethodStartGPSLocationListener = 0;
	public static final int MethodGoToMyLocation = 1;
	public static final int MethodTrackMyLocation = 2;
	public static final int MethodAddMarker = 3;
	public static final int MethodSetActivityTitle = 4;
	public static final int MethodUpdateScreen = 5;
	
	public class MyUpdateScreenIntentReceiver extends BroadcastReceiver {
		@Override
		public void onReceive(Context context, Intent intent) {
			int type = intent.getIntExtra("type", 0);
//			System.out.println("UpdateScreen.onReceive() type="+type+" "+isMapInCenter);
			if (type >= 1) {
				double lat = intent.getDoubleExtra("lat", 0);
				double lon = intent.getDoubleExtra("lon", 0);
				int zoom = intent.getIntExtra("zoom", PhysicMap.getZoomLevel());
				
				// for BigPlanet.addMarkersForDrawing() only
				if (type == 2) {
					//mAutoFollowRelativeLayout.setVisibility(View.VISIBLE);
					isFollowMode = false;
				}
				
				goToMyLocation(lat, lon, zoom);
//				Log.i("goToMyLocation(lon, lat)", "type="+type+", "+lon+", "+lat);
			} else if(type == -1) { // from MapControl::updateScreen
				// do nothing but update Activity Title
			} else{
				// center map
				centerMap();
			}
			// refresh the activity title
			setActivityTitle(BigPlanet.this);
		}
	}
	
	private void centerMap() {
		if (isFollowMode && !isMapInCenter) {
			if (currentLocation != null) {
				isMapInCenter = true;
				int zoom = PhysicMap.getZoomLevel();
				double latFix = currentLocation.getLatitude() + myGPSOffset.y*Math.pow(10, -5);
				double lonFix = currentLocation.getLongitude() + myGPSOffset.x*Math.pow(10, -5);
				tuding.android.bigplanettracks.maps.geoutils.Point p = GeoUtils.toTileXY(latFix, lonFix, zoom);
				tuding.android.bigplanettracks.maps.geoutils.Point off = GeoUtils.getPixelOffsetInTile(latFix, lonFix, zoom);
				mapControl.goTo((int) p.x, (int) p.y, zoom, (int) off.x, (int) off.y);
			}
		} else {
			// when showing search result, recording under non-auto-follow, etc.
			mapControl.invalidate();
		}
	}

	@Override
	public boolean onSearchRequested() {
		startSearch("", false, null, false);
		return true;
	}

	@Override
	public void onConfigurationChanged(Configuration newConfig) {
		super.onConfigurationChanged(newConfig);
		Paused_flag = false;
		//Log.i("FLOW", "onConfigurationChanged: "+Orientation_flag);
		configMapControl(mapControl.getPhysicalMap().getDefaultTile());
		if (isFollowMode && currentLocation != null) {
			goToMyLocation(currentLocation, PhysicMap.getZoomLevel());
		} else {
			mapControl.getPhysicalMap().reloadTiles();
			mapControl.invalidate();
		}
	}
	
	@Override
	protected void onStart() {
		super.onStart();
		Paused_flag = false;
		//Log.i("FLOW","onStart!");
		if (SDCARD_AVAILABLE) {
			if (isGPSTracking){
				//toggle_record_btn(0);
			} else{
				//toggle_record_btn(1);
			}
			if (isFollowMode){
				//mAutoFollowRelativeLayout.setVisibility(View.INVISIBLE);
			} else {
				//mAutoFollowRelativeLayout.setVisibility(View.VISIBLE);
			}
		}
	}
	
	@Override
	protected void onResume() {
		super.onResume();
		Paused_flag = false;
		
		
		//Log.i("FLOW","onResume! ori: "+this.getResources().getConfiguration().orientation);
		if (SDCARD_AVAILABLE) {
			//startGPSLocationListener();
			if (isFirstEntry) {
				isFirstEntry = false;
				isFollowMode = false;
				followMyLocation();
			}
			if(BigPlanet.isGPSTracking == false) {
				startGPSLocationListener();
				AutoPause.PauseGear = 0;
				AutoPause.First_start_delay_counter = 1;
				AutoPause.First_start_flag = true;
				autoPause.AP_enable();
				
				showRefreshGPS(false);
			}
			mapControl.getPhysicalMap().reloadTiles();
			
			if(Orientation_flag != getResources().getConfiguration().orientation) {
				Orientation_flag = getResources().getConfiguration().orientation;
				onConfigurationChanged(this.getResources().getConfiguration());
			}
		}
	}
	
	@Override
	public void onBackPressed() {
		if(isGPSTracking || isAutoRecording) {
			// exit to main screen
			Intent home = new Intent(Intent.ACTION_MAIN);  
			home.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);  
			home.addCategory(Intent.CATEGORY_HOME);  
			startActivity(home);
		} else if(true == isPressAgain) {
			Logex.message("FLOW", "finishGPSListener");
			finish();
		} else {
			setPressAgainFlag();
			Toast.makeText(this, R.string.DOUBLE_CLICK_TO_EXIT, Toast.LENGTH_SHORT).show();
		}
	}
	
	public static void setPressAgainFlag() {
		isPressAgain = true;
	}
	public static void resetPressAgainFlag() {
		isPressAgain = false;
	}
	
	
	@Override
	protected void onPause() {
		super.onPause();
		Paused_flag = true;
		Orientation_flag = this.getResources().getConfiguration().orientation;
		//this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
		Logex.message("FLOW","Paused! ori: "+Orientation_flag);
		//mapControl.getPhysicalMap().getTileResolver().clearCache();
		finishGPSLocationListener(); // release the GPS resources
		
		
	}
	
	@Override
	protected void onStop() {
		super.onStop();
		//Log.i("FLOW","Stoped!");
		if (SDCARD_AVAILABLE) 
			mapControl.getPhysicalMap().getTileResolver().clearCache();
	}
	
	@Override
	protected void onDestroy() {
		super.onDestroy();
		//if (SDCARD_AVAILABLE)
		//	mm.storeMarkersG_block();
		DBAdapter.close();
		DBAdapter = null;
		autoPause.AP_disable();
		locationManager.removeGpsStatusListener(autoPause.gpsListener);
		Logex.message("FLOW","onDestroy!");
		SmoothZoomEngine.sze = null; // release the variable
		SmoothZoomEngine.stop = true; // stop the thread
		TileLoader.stop = true; // stop the thread
		//mAutoFollowRelativeLayout = null;
		if (searchIntentReceiver != null) {
			unregisterReceiver(searchIntentReceiver);
			Preferences.putTile(mapControl.getPhysicalMap().getDefaultTile());
			Preferences.putOffset(mapControl.getPhysicalMap().getGlobalOffset());
		}
		if (updateScreenIntentReceiver != null) {
			unregisterReceiver(updateScreenIntentReceiver);
		}
		if(simpleReceiver != null) {
			unregisterReceiver(simpleReceiver);
		}
		if (textMessage != null) {
			textMessage.cancel();
		}
		
	}
	
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		switch (resultCode) {
		case RESULT_OK:
			if (requestCode == 0) {
				isFollowMode = true;
				disabledAutoFollow(BigPlanet.this);
				GeoBookmark bookmark = (GeoBookmark) data.getSerializableExtra(BOOKMARK_DATA);
				mapControl.getPhysicalMap().setDefTile(bookmark.getTile());
	
				Point offset = new Point();
				offset.set(bookmark.getOffsetX(), bookmark.getOffsetY());
				Preferences.putSourceId(bookmark.getTile().s);
				mapControl.getPhysicalMap().setGlobalOffset(offset);
				mapControl.getPhysicalMap().reloadTiles();
				mapControl.setMapSource(bookmark.getTile().s);
				mm.addBookMarker(bookmark.getTile(), bookmark.getOffsetX(), bookmark.getOffsetY(), BigPlanet.this.mapControl.getPhysicalMap().getWidth(), BigPlanet.this.mapControl.getPhysicalMap().getHeight(), (long)bookmark.getId());
				//Log.i("EVENT", "Show bookmarker: X: "+bookmark.getTile().x+" Y: "+bookmark.getTile().y+" x: "+bookmark.getOffsetX()+" y: "+bookmark.getOffsetY());
				showBookmarks = true;
				mapControl.updateScreen();
			} else if(requestCode == ActionPhotoRC) {
				String photoname = data.getStringExtra(ImageUploaderActivity.IMAGE_NAME);
				String photodesc = data.getStringExtra(ImageUploaderActivity.IMAGE_COMMENT);
				//Log.i("IMAGE", "I am back from IUA, and uri is "+photoname);
				final ActionBase newActionBase = new ActionBase();
		    	tuding.android.bigplanettracks.maps.geoutils.Point point_tmp2 = GeoUtils.toTileXY(currentLocation.getLatitude(), currentLocation.getLongitude(), PhysicMap.getZoomLevel());
		    	tuding.android.bigplanettracks.maps.geoutils.Point point_tmp1 = GeoUtils.getPixelOffsetInTile(currentLocation.getLatitude(), currentLocation.getLongitude(), PhysicMap.getZoomLevel());
		    	
				newActionBase.setOffsetX((int)point_tmp1.x);
				newActionBase.setOffsetY((int)point_tmp1.y);
				newActionBase.setLat((float)currentLocation.getLatitude());
				newActionBase.setLon((float)currentLocation.getLongitude());
				newActionBase.setTile(new RawTile((int)point_tmp2.x, (int)point_tmp2.y, mapControl.getPhysicalMap().getDefaultTile().z, mapControl.getPhysicalMap().getDefaultTile().s));
				newActionBase.getTile().s = mapControl.getPhysicalMap().getTileResolver().getMapSourceId();
				newActionBase.setDate(System.currentTimeMillis());
				newActionBase.setSpeed((int)currentLocation.getSpeed());
				newActionBase.setAltitude((int)currentLocation.getAltitude());
				newActionBase.setTrackID((int)currentTrackID);
				newActionBase.setName(photodesc);
				newActionBase.setUri(photoname);
				
				ActionNotes d = new ActionNotes();
				long actionID = d.saveActionPhoto(newActionBase);
				d.close();
				//Log.i("MARKER", "New action id is "+actionID);
				Place place = new Place();
				place.setLat(currentLocation.getLatitude());
				place.setLon(currentLocation.getLongitude());
				place.setLocation(currentLocation);
				MarkerManager.addMarker(place, PhysicMap.getZoomLevel(), MarkerManager.DrawMarkerOrTrack, MarkerManager.ACTION_PHOTO_MARKER, 2, actionID);
				mapControl.invalidate();
			} else if(requestCode==ActionPhotoUploadingWithLoc) {//Callback from uploading loc with photo
				try {
					String uristring = data.getStringExtra(ImageUploaderActivity.IMAGE_NAME);
					String photodesc = data.getStringExtra(ImageUploaderActivity.IMAGE_COMMENT);
					UploadLocation ul = new UploadLocation(BigPlanet.this, connectionHandler);
					ul.setImgUriStr(uristring);
					ul.setImgDesc(photodesc);
					ul.uploadLocation(currentLocation);
				} catch(Exception e) {
					e.printStackTrace();
					Toast.makeText(this, R.string.UNKNOWN_EXCEPTION, Toast.LENGTH_SHORT).show();
				}
			}
			break;
		default:
			break;
		}

	}

	/*
	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		switch (keyCode) {
		case KeyEvent.KEYCODE_BACK:
			// if the current mode is SELECT_MODE - change to ZOOM_MODE
			if (mapControl.getMapMode() == MapControl.SELECT_MODE) {
				mapControl.setMapMode(MapControl.ZOOM_MODE);
				return true;
			} else if (isGPSTracking && isAutoRecording) {
				Intent intentHome = new Intent("android.intent.action.MAIN");
				intentHome.addCategory("android.intent.category.HOME");
				startActivity(intentHome);
				return true;
			}
		default:
			return super.onKeyDown(keyCode, event);
		}
	}*/
	
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		super.onCreateOptionsMenu(menu);
		return true;
	}

	@Override
	public boolean onPrepareOptionsMenu(Menu menu) {
		resetPressAgainFlag();
		mapControl.clearInfoWin();
		menu.clear();
		SubMenu sub;
		
		if(showSearchResults == false){
		    menu.add(1, 11, 0, R.string.SEARCH_MENU).setIcon(R.drawable.search);
		} else {
			menu.add(1, 12, 0, R.string.SEARCH_RESULT_CLEAN).setIcon(R.drawable.search);
		}

		boolean hasSavedTracksG = checkMarkers(MarkerManager.savedTrackG);
		boolean hasMarkersDB = checkMarkers(MarkerManager.markersDB);
		if (hasSavedTracksG || hasMarkersDB) {
			sub = menu.addSubMenu(6, 6, 0, R.string.TRACK_MENU).setIcon(R.drawable.track_manage);
			sub.add(6, 61, 0, R.string.MANAGE_TRACK_MENU);
			if (hasSavedTracksG)
				sub.add(6, 62, 1, R.string.CLEAR_RECORDED_TRACK_MENU);
			if (hasMarkersDB)
				sub.add(6, 63, 2, R.string.CLEAR_LOADED_TRACK_MENU);
			if (hasSavedTracksG && hasMarkersDB)
				sub.add(6, 64, 3, R.string.CLEAR_ALL_TRACKS_MENU);
		} else {
			menu.add(6, 61, 0, R.string.TRACK_MENU).setIcon(R.drawable.track_manage);
		}

		sub = menu.addSubMenu(2, 2, 0, R.string.BOOKMARKS_MENU).setIcon(R.drawable.bookmark);
		sub.add(2, 21, 0, R.string.BOOKMARK_ADD_MENU);
		sub.add(2, 22, 1, R.string.BOOKMARKS_VIEW_MENU);
		if(showBookmarks) sub.add(2, 23, 2, R.string.BOOKMARKS_LOADED_CLEAN);

		//menu.add(5, 51, 0, R.string.SQLiteDB_MENU).setIcon(R.drawable.map);
		menu.add(5, 46, 0, R.string.CONNECT_TO_TUDING_MENU).setIcon(R.drawable.map);
		menu.add(3, 31, 0, R.string.UPLOAD_MY_LOCATION).setIcon(R.drawable.mapmaker32);
		
		// More menu
		sub = menu.addSubMenu(4, 4, 0, R.string.TOOLS_MENU).setIcon(R.drawable.tools);
//		sub.clearHeader();
		//sub.add(4, 41, 0, R.string.NETWORK_MODE_MENU);
		sub.add(4, 41, 0, R.string.GPSUPDATEINTERVAL);
		sub.add(4, 43, 1, R.string.AUTORECORD_INTERVAL);
		sub.add(4, 44, 2, R.string.AUTORECORD_PRIVACY);
		sub.add(4, 42, 3, R.string.CACHE_MAP_MENU);
		sub.add(4, 45, 4, R.string.MAP_SOURCE_MENU);
		//sub.add(4, 46, 5, R.string.CONNECT_TO_TUDING_MENU);
		//sub.add(4, 51, 5, R.string.SQLiteDB_MENU);
		//sub.add(4, 52, 7, R.string.EXIT);
		sub.add(4, 49, 10, R.string.ABOUT_MENU);

		//boolean useNet = Preferences.getUseNet();
		//menu.findItem(42).setEnabled(useNet);
		return true;
	}
	
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		hideMessage();
		switch (item.getItemId()) {
		case 11:
			showSearch();
			break;
		case 12:
			showSearchResults = false;
			mapControl.invalidate();
			break;
		case 21:
			switchToBookmarkMode();
			break;
		case 22:
			showAllGeoBookmarks();
			break;
		case 23:
			cleanAllGeoBookmarks();
			break;
		case 31:
			//followMyLocation();
			uploadCurrentLocaltion();
			//selectMapSource();
			break;
		case 41:
			//selectNetworkMode();
			showGPSConfigDialog();
			break;
		case 42:
			showMapSaver();
			break;
		case 43:
			setAutoUploadInterval();
			break;
		case 44:
			setAutoUploadPrivacy();
			break;
		case 45:
			selectMapSource();
			break;
		case 46:
			connectToTuding();
			break;
		case 49:
			showAbout();
			break;
		case 51:
			selectSQLiteDBFile();
			break;
		case 52:
			if(!isGPSTracking){ finish();}
			else Toast.makeText(this, R.string.STOP_TRACKING_BEFORE_EXIT, Toast.LENGTH_SHORT).show();
			break;
		case 61: //import and browse tracks from SD card
			importAndBrowseTracks();
			break;
		case 62: //clear track
			clearSaveTracksG();
			break;
		case 63: //clear track
			clearMarkerDB();
			break;
		case 64: //clear all tracks on the map
			clearMap();
			break;

		}
		return false;
	}

	private boolean checkMarkers(List<Marker> list) {
		if(list.size()>0){
			return true;
		}
		return false;
	}
	
	public void onLongClickRoutine(int x, int y) {
		hideMessage();
		//Log.i("BIGPLANET", "1 Now deftile x: "+mapControl.getPhysicalMap().getDefaultTile().x+" y: "+mapControl.getPhysicalMap().getDefaultTile().y);
		final GeoBookmark newGeoBookmark = new GeoBookmark();
		// we need tune the offset for the bookmark
		int tunedoffsetX = mapControl.getPhysicalMap().getGlobalOffset().x - x;
		int tunedoffsetY = mapControl.getPhysicalMap().getGlobalOffset().y - y;
		//Log.i("BIGPLANET", "2 Now deftile x: "+mapControl.getPhysicalMap().getDefaultTile().x+" y: "+mapControl.getPhysicalMap().getDefaultTile().y);
		int tunedTileX = mapControl.getPhysicalMap().getDefaultTile().x;
		int tunedTileY = mapControl.getPhysicalMap().getDefaultTile().y;
		int tunedTileZ = mapControl.getPhysicalMap().getDefaultTile().z;
		int tunedTileS = mapControl.getPhysicalMap().getDefaultTile().s;
		//Log.i("BIGPLANET", "3 Now deftile x: "+mapControl.getPhysicalMap().getDefaultTile().x+" y: "+mapControl.getPhysicalMap().getDefaultTile().y);
		//Log.i("BIGPLANET", "org offsetX: "+mapControl.getPhysicalMap().getGlobalOffset().x+" Y:"+mapControl.getPhysicalMap().getGlobalOffset().y);
		//Log.i("BIGPLANET", "newoffset x: " + x+" y: "+y);
		// make sure the offset is in (-256,0] otherwise, the deftile is not the lefttop one, and reload will only cover a part instead of whole tiles
		
		while(tunedoffsetX <= -256) {
			tunedTileX++;
		    tunedoffsetX += 256;
		}
		while(tunedoffsetX > 0) {
			tunedTileX--;
			tunedoffsetX -= 256;
		}
		while(tunedoffsetY <= -256) {
			tunedTileY++;
			tunedoffsetY += 256;
		}
		while(tunedoffsetY > 0) {
			tunedTileY--;
			tunedoffsetY -= 256;
		}
		
		//Log.i("BIGPLANET", "4 Now deftile x: "+mapControl.getPhysicalMap().getDefaultTile().x+" y: "+mapControl.getPhysicalMap().getDefaultTile().y);
		//Log.i("BIGPLANET", "tunedoffsetx: "+tunedoffsetX+" tunedoffsetY: "+tunedoffsetY);
		//Log.i("BIGPLANET", "tunedTileX: "+tunedTileX+" tunedTileY: "+tunedTileY);
		//Log.i("BIGPLANET", "Now deftile x: "+mapControl.getPhysicalMap().getDefaultTile().x+" y: "+mapControl.getPhysicalMap().getDefaultTile().y);
		//Log.i("BIGPLANET", "New global offset x: "+mapControl.getPhysicalMap().getGlobalOffset().x);
		newGeoBookmark.setOffsetX(tunedoffsetX);
		newGeoBookmark.setOffsetY(tunedoffsetY);
		newGeoBookmark.setTile(new RawTile(tunedTileX, tunedTileY, tunedTileZ, tunedTileS));
		newGeoBookmark.getTile().s = mapControl.getPhysicalMap().getTileResolver().getMapSourceId();

		AddBookmarkDialog.show(BigPlanet.this, newGeoBookmark,
				new OnDialogClickListener() {
					@Override
					public void onCancelClick() {
					}
					
					@Override
					public void onOkClick(Object obj) {
						GeoBookmark geoBookmark = (GeoBookmark) obj;
						DAO d = new DAO(BigPlanet.this);
						long tmp_long = d.saveGeoBookmark(geoBookmark);
						mapControl.setMapMode(MapControl.ZOOM_MODE);
						// add a start to indicate done
						mm.addBookMarker(geoBookmark.getTile(), geoBookmark.getOffsetX(), geoBookmark.getOffsetY(), BigPlanet.this.mapControl.getPhysicalMap().getWidth(), BigPlanet.this.mapControl.getPhysicalMap().getHeight(), tmp_long);
						//Log.i("EVENT", "add bookmarker: X: "+geoBookmark.getTile().x+" Y: "+geoBookmark.getTile().y+" x: "+geoBookmark.getOffsetX()+" y: "+geoBookmark.getOffsetY());
						showBookmarks = true;
						mapControl.invalidate(); 
					}
				});
	}

	/**
	 * Sets the size of maps and other properties
	 */
	private void configMapControl(RawTile tile) {
		WindowManager wm = this.getWindowManager();
		Display display = wm.getDefaultDisplay();
		int width = display.getWidth();
		int height = display.getHeight();
		
		height = height - (int)(48*(density)); // minus the space of the status bar
		
		if (mapControl == null) {
			//identifier = getString(R.string.ABOUT_URL);
			mapControl = new MapControl(this, width, height, tile, mm);
			mapControl.setOnMapLongClickListener(new OnMapLongClickListener() {

				@Override
				public void onMapLongClick(int x, int y) {
					onLongClickRoutine(x, y);
				}
			});
		} else {
			mapControl.setSize(width, height);
		}
		mapControl.updateZoomControls();
		
		setContentView(mapControl, new ViewGroup.LayoutParams(width, height));
		getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.title);
	}

	private void importAndBrowseTracks(){
		Intent importTrackIntent = new Intent(this, TrackTabViewActivity.class);
		startActivity(importTrackIntent);
	}
	
	
	
	
	
	
	
	// for Recoring and Uploading Routine
	private void stopRecordingandUploading() {
		try {
			registerAlarm.cancel(registerPendingIntent);
			cancelAlarm.cancel(cancelPendingIntent);
		} catch(Exception e) {
			e.printStackTrace();
		}
		if(simpleLocationService != null) {
			locationManagerforRecord.removeUpdates(simpleLocationService);
		}
		
		
		Intent intent = new Intent(this, SimpleLocationService.class);
		this.stopService(intent);
	}
	

	public static SimpleLocationService simpleLocationService;
	public static Handler recordinganduploadingHandler;
	public static Location clocationRecord;
	public static String default_provider = LocationManager.GPS_PROVIDER;
	public AlarmManager registerAlarm;
	public AlarmManager cancelAlarm;
	public PendingIntent registerPendingIntent;
	public PendingIntent cancelPendingIntent;
	
	
	
	private void startRecordingandUploading() {
		// TODO 
		Criteria criteria = new Criteria();
		criteria.setAccuracy(Criteria.ACCURACY_FINE);
		criteria.setAltitudeRequired(false);
		criteria.setSpeedRequired(false);
		criteria.setBearingRequired(false);
		criteria.setCostAllowed(true);
		criteria.setPowerRequirement(Criteria.POWER_LOW);
		default_provider = locationManager.getBestProvider(criteria, true);
		Logex.message("Recording", "startRecordingandUploading");
		if (default_provider != null) {
				
			long timeintval = Preferences.getAutoRecordIntervalVal();
			//timeintval = 10000; // for test
			
//			if(timeintval % AlarmManager.INTERVAL_FIFTEEN_MINUTES == 0) {
//				// more power efficient
//				Log.message("Recording", "using setInexactRepeating");
//				registerAlarm.setInexactRepeating (AlarmManager.RTC_WAKEUP, 0, timeintval, registerPendingIntent);
//				cancelAlarm.setInexactRepeating (AlarmManager.RTC_WAKEUP, 60000, timeintval, cancelPendingIntent);
//			} else {
			// setInexactRepeating is not working as expected: both register and cancel may schedule to the same time
				Logex.message("Recording", "using setRepeating");
				registerAlarm.setRepeating(AlarmManager.RTC_WAKEUP, 0, timeintval, registerPendingIntent);
				cancelAlarm.setRepeating(AlarmManager.RTC_WAKEUP, 60000, timeintval, cancelPendingIntent); // every minutes cancel the alarm to avoid the deadlock
//			}
		}
		else { // gps and network are both disabled
			Logex.message("Recording", "No location provider");
			Toast.makeText(this, R.string.msg_unable_to_get_current_location, Toast.LENGTH_LONG).show();
		}
		
		Logex.message("Recording", "startingservice");
		Intent intent = new Intent(this, SimpleLocationService.class);
		this.startService(intent);
		
	}
	
	private void startGPSLocationListener() {
		
		if(!locationManager.isProviderEnabled(android.location.LocationManager.GPS_PROVIDER)) {
			Toast.makeText(BigPlanet.this, getString(R.string.NEEDENABLEGPS), 
					Toast.LENGTH_SHORT).show();
		}
		
		Criteria criteria = new Criteria();
		criteria.setAccuracy(Criteria.ACCURACY_FINE);
		//criteria.setAccuracy(Criteria.ACCURACY_COARSE  );
		criteria.setAltitudeRequired(false);
		criteria.setSpeedRequired(false);
		criteria.setBearingRequired(false);
		criteria.setCostAllowed(true);
		criteria.setPowerRequirement(Criteria.POWER_LOW);
		String provider = locationManager.getBestProvider(criteria, true); //gps
		Location location = null;
		
		if (provider != null) {
			location = locationManager.getLastKnownLocation(provider);
		} else { // gps and network are both disabled
			Toast.makeText(this, R.string.msg_unable_to_get_current_location, Toast.LENGTH_SHORT).show();
			BigPlanet.locationProvider = null;
		}
		
		if(location == null) {
			location = new Location("");
			location.setLatitude(31.207574); 
			location.setLongitude(121.586643);
		}

		if(currentLocation == null)	currentLocation = location;
		currentTimeMillis = System.currentTimeMillis();

		/* GPS_PROVIDER */
		if (gpsLocationListener == null) {
			gpsLocationListener = new MyLocationService();
			gpsLocationListener.registerSensor(this);
			// LocationManager.GPS_PROVIDER = "gps"
			provider = LocationManager.GPS_PROVIDER;
			try {
				locationManager.requestLocationUpdates(provider, minTime, minDistance, gpsLocationListener);
				Logex.message("Location", provider +" requestLocationUpdates() "+ minTime +" "+ minDistance);
			} catch (RuntimeException e) {
			}
		}

		/* NETWORK_PROVIDER */
		if (networkLocationListener == null) {
			networkLocationListener = new MyLocationService();
			networkLocationListener.registerSensor(this);
			// LocationManager.NETWORK_PROVIDER = "network"
			provider = LocationManager.NETWORK_PROVIDER;
			try {
				locationManager.requestLocationUpdates(provider, 0, 0, networkLocationListener);
				Logex.message("Location", provider +" requestLocationUpdates() "+ 0 +" "+ 0);
			} catch (RuntimeException e) {
			}
		}
	}
	
	protected void finishGPSLocationListener() {
		new Thread("finishGPS") {
			public void run() {
				if (!isGPSTracking) {
					if (locationManager != null) {
						if (networkLocationListener != null) {
							networkLocationListener.unregisterSensor();
							locationManager.removeUpdates(networkLocationListener);
						}
						if (gpsLocationListener != null) {
							gpsLocationListener.unregisterSensor();
							locationManager.removeUpdates(gpsLocationListener);
						}
						
						networkLocationListener = null;
						gpsLocationListener = null;
						BigPlanet.locationProvider = null;
					}
					autoPause.AP_disable();
					
					updateLocationUpdateHandler.removeMessages(2); // MyLocationService use sendMessageDelayed(), so need remove update for later

				}
			}
		}.start();
	}
	
	public static MyLocationService gpsLocationListener;
	public static MyLocationService networkLocationListener;
	protected final static int minTime = 3000; // ms
	protected final static int BikeIntvTime = 6000; 
	protected final static int CarIntvTime = 10000; // when high speed drive, 10s interval, speed>20m/s or 72km/h
	protected final static int FlyIntvTime = 20000; // when fly, 20s per update location
	protected static int currentTimeInterval = 4000;
	//protected final static float minDistance = 10; // m
	protected final static float minDistance = 0; // m
	
	

	
	private void followMyLocation() {
		if (!isFollowMode) {
			enabledAutoFollow(this);
		} else {
			disabledAutoFollow(this);
		}
	}
	
	private void toggleRecord(){
		if(mapControl != null) {
			int old_R, new_R;
		
			if(isAutoRecording){ 
				old_R = R.drawable.recording;
				new_R = R.drawable.record;
			} else {
				old_R = R.drawable.record;
				new_R = R.drawable.recording;
			}
			//Log.i("FLOW", "toggle_record_btn");
			LinearLayout llv = (LinearLayout)mapControl.findViewById(old_R);
			if(llv == null) llv = (LinearLayout)mapControl.findViewById(new_R);
			try {
				Button btn = (Button) llv.findViewById(R.id.launchbar_button_item);
				btn.setCompoundDrawablesWithIntrinsicBounds(0, new_R, 0, 0);
				isAutoRecording = !isAutoRecording;
			}catch(Exception e){
				e.printStackTrace();
			}

		}
	}
	
	
	private void autoRecording() {
		if (!isAutoRecording) {
			// TODO
			if(!Preferences.isLogined()) {
				Toast.makeText(this, this.getString(R.string.NEED_LOGIN), Toast.LENGTH_LONG).show();
				return;
			}
			
			if(connectivityManager != null) {
				NetworkInfo info = connectivityManager.getActiveNetworkInfo();
				if(info == null || !info.isConnected()) {
					Toast.makeText(this, this.getString(R.string.CONNECTIVITYPROBLEM), Toast.LENGTH_LONG).show();
					return;
				}
			} else {
				Toast.makeText(this, this.getString(R.string.NOCONNECTIVITY), Toast.LENGTH_LONG).show();
				return;
			}
			// a dialog to make sure that user wants to start recording
			String[] ary = getResources().getStringArray(R.array.uploadinterval);
			String[] pary = getResources().getStringArray(R.array.record_privacy);

			Logex.message("Recording", ""+Preferences.getAutoRecordIntervalIndex());
			Logex.message("Recording", ""+ary[Preferences.getAutoRecordIntervalIndex()]);
			String intro = MessageFormat.format((String)getText(R.string.AUTORECORD_INTRO),
					"", ary[Preferences.getAutoRecordIntervalIndex()], pary[Preferences.getTudingUploadPrivacy()]);

			new AlertDialog.Builder(BigPlanet.this).setTitle(R.string.AUTORECORD_TITLE)
			.setMessage(intro) // TODO:
			
			.setPositiveButton(
					R.string.OK_LABEL,
					new DialogInterface.OnClickListener() {
						public void onClick(DialogInterface dialog,
								int whichButton) {
							toggleRecord();
							// only need coarse localtion info
							try{
								if(!locationManager.isProviderEnabled(android.location.LocationManager.GPS_PROVIDER)) {
									Toast.makeText(BigPlanet.this, getString(R.string.MAYNEEDENABLEGPS), Toast.LENGTH_LONG).show();
								}
								startRecordingandUploading();								
							} catch(Exception e) {
								e.printStackTrace();
							}
						}
					})
			
			.setNegativeButton(
					R.string.CANCEL_LABEL,
					new DialogInterface.OnClickListener() {
				        public void onClick(DialogInterface dialog,
						        int whichButton) {
				        }
			        })
			.show();
		} else {
			stopRecordingandUploading();
			toggleRecord();
		}
	}
	
	private void uploadCurrentLocaltion(){
		if(currentLocation.hasAccuracy()) {
			UploadLocation ul = new UploadLocation(BigPlanet.this, connectionHandler);
			ul.uploadLocation(currentLocation);
		} else {
			Toast.makeText( BigPlanet.this,	getString(R.string.GPSISWORKING), Toast.LENGTH_SHORT).show();
		}
	}

	private void goToMyLocation(Location location, int zoom) {
		double lat = location.getLatitude();
		double lon = location.getLongitude();
		goToMyLocation(lat, lon, zoom);
	}
	

	private void goToMyLocation(double lat, double lon, int zoom) {
		Location location = new Location("");
		location.setLatitude(lat);
		location.setLongitude(lon);
		double latFix = lat + myGPSOffset.y*Math.pow(10, -5);
		double lonFix = lon + myGPSOffset.x*Math.pow(10, -5);
		if(Paused_flag == false) {
		    tuding.android.bigplanettracks.maps.geoutils.Point p = GeoUtils.toTileXY(latFix, lonFix, zoom);
		    tuding.android.bigplanettracks.maps.geoutils.Point off = GeoUtils.getPixelOffsetInTile(latFix, lonFix, zoom);
		    mapControl.goTo((int) p.x, (int) p.y, zoom, (int) off.x, (int) off.y);
		}
		
		Place place = new Place();
		place.setLat(latFix);
		place.setLon(lonFix);
		place.setLocation(location);
		MarkerManager.addMarker(place, zoom, MarkerManager.DrawMarkerOrTrack, MarkerManager.MY_LOCATION_MARKER);
	}
	
	private void trackMyLocation(Location location, int zoom) {
		double latFix = location.getLatitude() + myGPSOffset.y*Math.pow(10, -5);;
		double lonFix = location.getLongitude() + myGPSOffset.x*Math.pow(10, -5);
		if(Paused_flag == false) {
		    tuding.android.bigplanettracks.maps.geoutils.Point p = GeoUtils.toTileXY(latFix, lonFix, zoom);
		    tuding.android.bigplanettracks.maps.geoutils.Point off = GeoUtils.getPixelOffsetInTile(latFix, lonFix, zoom);
		    //Log.i("EVENT", "trackMyLocation "+off.x+" "+ off.y);
		    mapControl.goTo((int) p.x, (int) p.y, zoom, (int) off.x, (int) off.y);
		}

		Place place = new Place();
		place.setLat(latFix);
		place.setLon(lonFix);
		place.setLocation(location);
		MarkerManager.addMarker(place, zoom, MarkerManager.DrawMarkerOrTrack, MarkerManager.MY_LOCATION_MARKER);		
		
	}
	
	private void addMarker(Location location, int zoom) {
		double latFix = location.getLatitude() + myGPSOffset.y*Math.pow(10, -5);;
		double lonFix = location.getLongitude() + myGPSOffset.x*Math.pow(10, -5);
		Place place = new Place();
		place.setLat(latFix);
		place.setLon(lonFix);
		place.setLocation(location);
		MarkerManager.addMarker(place, zoom, MarkerManager.DrawMarkerOrTrack, MarkerManager.MY_LOCATION_MARKER);
	}
	
	public static void addMarkersForDrawing(Context context, List<Place> placeList, int trackType) {
		//Log.i("Message", "At addMarkerForDrawing...");
		
		int zoom = PhysicMap.getZoomLevel();
		//double latTotal = 0;
		//double lonTotal = 0;
		double latMax = -89.9999;
		double latMin = 89.9999;
		double lonMax = -179.9999;
		double lonMin = 179.9999;

		for (int i=0;i<placeList.size();i++) {
			Place place = placeList.get(i);
			double lat = place.getLat();
			double lon = place.getLon();
			//latTotal += lat;
			//lonTotal += lon;
			if(latMax < lat) latMax = lat;
			if(latMin > lat) latMin = lat;
			if(lonMax < lon) lonMax = lon;
			if(lonMin > lon) lonMin = lon;
			//Log.i("LL", "lat: "+lat+" lon: "+lon+" latMax: "+latMax+" latMin: "+latMin+" lonMax: "+lonMax+" lonMin: "+lonMin);
			place.setLat(lat);
			place.setLon(lon);
			if(place.getActionType() != 0 && place.getActionID() != 0) {
				//Log.i("LL","addMarkersForDrawing type is: "+place.getActionType());
				int imageType = MarkerManager.MY_LOCATION_MARKER;
				if(place.getActionType() == 1) imageType = MarkerManager.ACTION_NOTES_MARKER;
				else if(place.getActionType() == 2) imageType = MarkerManager.ACTION_PHOTO_MARKER;
				MarkerManager.addMarker(place, zoom, trackType, imageType, place.getActionType(), place.getActionID());
			} else {
				//Log.i("LL","addMarkersForDrawing  i:"+i);
			    MarkerManager.addMarker(place, zoom, trackType, MarkerManager.MY_LOCATION_MARKER);
			}
		} 
		fittingZoom = getZoomLevelByLL(latMax, latMin, lonMax, lonMin); 
		// update markers
		for(Marker marker_DB : MarkerManager.markersDB){
			//Log.i("LL", "addMarkersForDrawing zoom is "+fittingZoom);
			MarkerManager.updateParams(marker_DB, fittingZoom);
		}
		//Log.i("ZOOM", "fittingZoom should be "+fittingZoom);
		if (trackType == MarkerManager.DrawTrackFromDB) {
			autoDisplayDB = true;
			autoDisplayDBforMarker = true;
		}
		if (!isDBdrawclear) {
			isDBdrawclear = true;
		}
		Intent i = new Intent(BigPlanet.UpdateScreenAction);
		if (trackType == MarkerManager.DrawTrackFromDB) {
			//autoDisplayDB_Lat = latTotal/placeList.size();
			//autoDisplayDB_Lon = lonTotal/placeList.size();
			autoDisplayDB_Lat = (latMax + latMin) / 2;
			autoDisplayDB_Lon = (lonMax + lonMin) / 2;
			//Log.i("LL", autoDisplayDB_Lon+", "+autoDisplayDB_Lat);
			clearYellowPersonMarker = true;
			i.putExtra("type", 2);
			i.putExtra("lat", autoDisplayDB_Lat);
			i.putExtra("lon", autoDisplayDB_Lon);
			i.putExtra("zoom", fittingZoom);
		}
		context.sendBroadcast(i);
	}
	
	private void clearSaveTracksG() {
		if (!mm.clearSavedTracksG()) {
			textMessage = Toast.makeText(this, R.string.clearSaveTrackFales,
					Toast.LENGTH_LONG);
			textMessage.show();
		}
		mapControl.invalidate();
	}
	
	private void clearMarkerDB() {
		mm.clearMarkersDB();
		mapControl.invalidate();
	}
	
	private void clearMap() {
		clearSaveTracksG();
		clearMarkerDB();
	}
	

	private void showSearch() {
		onSearchRequested();
	}
	
	public void connectToTuding() {
		/*
		//gen excpetion by write 
		String outFileName = "/sdfe/dd" + GMOT.DATABASE_NAME;

        File dir = new File(outFileName);
        if(dir.exists())
            return;

        dir = new File(SQLLocalStorage.OFFSET_PATH);

        if (!dir.exists())
            dir.mkdir();
        dir = null;

        InputStream input = null;
        OutputStream output = null;


        try {
            output = new FileOutputStream(outFileName);

            // 拷贝到输出流
            byte[] buffer = new byte[2048];
            int length;
            while ((length = input.read(buffer)) > 0) {
                output.write(buffer, 0, length);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
        	try {
                output.flush();
                output.close();
            } catch (IOException e) {
            }
            try {
                input.close();
            } catch (IOException e) {
            }
        }*/
		
		
		
		
		
		if (!Preferences.isLogined()) {
			final Dialog paramsDialog = new Dialog(this);
			final View v = View.inflate(this, R.layout.connect_to_tuding, null);
			final EditText username = (EditText)v.findViewById(R.id.usernameValue);
			username.setText(Preferences.getUsername());
			final EditText password = (EditText)v.findViewById(R.id.passwordValue);
			password.setText(Preferences.getPassword());

			final Button confirmBtn = (Button) v.findViewById(R.id.confirmTudingBtn);
			confirmBtn.setEnabled(true);
			confirmBtn.setClickable(false);
			confirmBtn.setOnClickListener(new OnClickListener() {

				public void onClick(View arg0) {
					
					TextView usernameValue = (TextView) v.findViewById(R.id.usernameValue);
					TextView passwordValue = (TextView) v.findViewById(R.id.passwordValue);
					
					if(usernameValue.getText().length() == 0 || passwordValue.getText().length() == 0) {
						Toast.makeText( BigPlanet.this,	getString(R.string.login_failed), Toast.LENGTH_SHORT).show();
					} else {
						Preferences.putUsername(usernameValue.getText().toString());
						Preferences.putPassword(passwordValue.getText().toString());
	
						final BaseConnection conn = new BaseConnection();
						conn.setHandler(connectionHandler);
						conn.setUsername(usernameValue.getText().toString());
						conn.setPassword(passwordValue.getText().toString());
											
						final CharSequence strTitle = 
							getString(R.string.login_title);
						final CharSequence strBody = 
							getString(R.string.login_body);
	
						// show Progress Dialog
						loginDialog = ProgressDialog.show(
								BigPlanet.this,
								strTitle,
								strBody,
								true,
								true,
								new DialogInterface.OnCancelListener() {
				                    public void onCancel(DialogInterface dialog) {
				                    	conn.stopConnection();
				                    }
								}
						);
						
						conn.connect();
	
						paramsDialog.dismiss();
					}
				}
			});
			
			final Button registerBtn = (Button) v.findViewById(R.id.RegisterBtn);
			registerBtn.setEnabled(true);
			registerBtn.setClickable(false);
			registerBtn.setOnClickListener(new OnClickListener() {
				public void onClick(View arg0) {
					Intent i = new Intent(Intent.ACTION_VIEW);
					i.setData(Uri.parse(SERVER_ADDRESS+"do.php?ac=69b4871c8fa5fc4d08887b7288ee47d1"));
					startActivity(i);
				}

			});

			final Button cancelBtn = (Button) v.findViewById(R.id.cancelTudingBtn);
			cancelBtn.setEnabled(true);
			cancelBtn.setClickable(false);
			cancelBtn.setOnClickListener(new OnClickListener() {

				public void onClick(View arg0) {
					paramsDialog.dismiss();
				}

			});

			paramsDialog.setTitle(R.string.CONNECT_TO_TUDING_MENU);
			paramsDialog.setCanceledOnTouchOutside(false);
			paramsDialog.setCancelable(true);
			paramsDialog.setContentView(v);
			paramsDialog.show();
		} else {
			final Dialog paramsDialog = new Dialog(this);
			final View v = View.inflate(this, R.layout.tuding_login_info, null);
			final TextView username = (TextView)v.findViewById(R.id.usernameValue);
			username.setText(Preferences.getUsername());

			final Button confirmBtn = (Button) v.findViewById(R.id.confirmTudingBtn);
			confirmBtn.setEnabled(true);
			confirmBtn.setClickable(false);
			confirmBtn.setOnClickListener(new OnClickListener() {
				public void onClick(View arg0) {
					paramsDialog.dismiss();
				}
			});

			final Button logoutBtn = (Button) v.findViewById(R.id.logoutTudingBtn);
			logoutBtn.setEnabled(true);
			logoutBtn.setClickable(false);
			logoutBtn.setOnClickListener(new OnClickListener() {
				public void onClick(View arg0) {
					Preferences.putUsername("");
					Preferences.putPassword("");
					Preferences.setLogined(false);
					paramsDialog.dismiss();
				}
			});

			paramsDialog.setTitle(R.string.CONNECT_TO_TUDING_MENU);
			paramsDialog.setCanceledOnTouchOutside(false);
			paramsDialog.setCancelable(true);
			paramsDialog.setContentView(v);
			paramsDialog.show();
		}
	}
	
	private void showAbout() {
		final String url = getString(R.string.ABOUT_URL);
		String about = getString(R.string.ABOUT_MESSAGE).replace("{url}", url);
		TextView tv = new TextView(this);
		tv.setLinksClickable(true);
		tv.setAutoLinkMask(Linkify.EMAIL_ADDRESSES);
		tv.setAutoLinkMask(Linkify.WEB_URLS);
		tv.setGravity(Gravity.CENTER);
		tv.setText(about);
		tv.setTextSize(12f);
		String versionName = "";
		String packageNum = "";
		try {
			String packageName = BigPlanet.class.getPackage().getName();
			PackageInfo info = getPackageManager().getPackageInfo(packageName, 0);
			versionName = info.versionName;
			packageNum = SHA1Hash.encode(info.packageName);
			packageNum = packageNum.substring(0,2);
		} catch (PackageManager.NameNotFoundException e) {
		} catch (NoSuchAlgorithmException e) {
		} catch (UnsupportedEncodingException e) {
		}
		ScrollView scrollPanel = new ScrollView(this);
		scrollPanel.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,
				LayoutParams.FILL_PARENT));
		scrollPanel.addView(tv);
		try {
			if ((Integer.parseInt(packageNum)-8)%10==0) {
				new AlertDialog.Builder(this)
				.setTitle(getString(R.string.ABOUT_TITLE)+" "+versionName)
				.setView(scrollPanel).setIcon(R.drawable.globe)
				.setPositiveButton(
						R.string.OK_LABEL,
						new DialogInterface.OnClickListener() {
							public void onClick(DialogInterface dialog,
									int whichButton) {
							}
						})
				.setNeutralButton(
						R.string.website,
						new DialogInterface.OnClickListener() {
							public void onClick(DialogInterface dialog,
									int whichButton) {
								Intent i = new Intent(Intent.ACTION_VIEW);
								i.setData(Uri.parse(url));
								startActivity(i);
							}
						})
				.show();
			}
		} catch (NumberFormatException e) {
		}
	}

	private void showAllGeoBookmarks() {
		Intent intent = new Intent();
		intent.setClass(this, AllGeoBookmarks.class);
		startActivityForResult(intent, 0);
	}
	
	private void cleanAllGeoBookmarks() {
		showBookmarks = false;
		mm.clearMarkerManager();
		mapControl.invalidate();
	}

	private void switchToBookmarkMode() {
		if (mapControl.getMapMode() != MapControl.SELECT_MODE) {
			mapControl.setMapMode(MapControl.SELECT_MODE);
			showMessage();
		}
	}

	private void showMessage() {
		textMessage = Toast.makeText(this, R.string.SELECT_OBJECT_MESSAGE,
				Toast.LENGTH_LONG);
		textMessage.show();
	}

	private void hideMessage() {
		if (textMessage != null) {
			textMessage.cancel();
		}
	}

	/**
	 * Displays a dialog for caching maps in a given radius
	 */
	private void showMapSaver() {
		MapSaverUI mapSaverUI = new MapSaverUI(this, 
				PhysicMap.getZoomLevel(), 
				mapControl.getPhysicalMap().getAbsoluteCenter(), 
				mapControl.getPhysicalMap().getTileResolver().getMapSourceId());
		mapSaverUI.show();
	}
	
	private void setAutoUploadInterval() {
		Logex.message("Recording", "setAutoUploadInterval");
		Dialog dl = new Dialog(BigPlanet.this);
		AlertDialog.Builder builder = new AlertDialog.Builder(this);
		builder.setTitle(this.getString(R.string.AUTORECORD_INTERVAL));
		builder.setSingleChoiceItems(R.array.uploadinterval, Preferences.getAutoRecordIntervalIndex(), new DialogInterface.OnClickListener() {
			@Override
			public void onClick(DialogInterface dialog, int which) {
				Logex.message("Recording", "You select "+which);
				Preferences.setAutoRecordIntervalIndex(which);
			}
		});
		builder.setPositiveButton(this.getString(R.string.OK_LABEL), new DialogInterface.OnClickListener(){
			@Override
			public void onClick(DialogInterface dialog, int which) {
				// disable uploading and enable again
				stopRecordingandUploading();
				startRecordingandUploading();
			}
		});
		dl = builder.create();
		dl.show();
	}
	
	private void setAutoUploadPrivacy() {
		Logex.message("Recording", "setAutoUploadInterval");
		Dialog dl = new Dialog(BigPlanet.this);
		AlertDialog.Builder builder = new AlertDialog.Builder(this);
		builder.setTitle(this.getString(R.string.AUTORECORD_PRIVACY));
		builder.setSingleChoiceItems(R.array.location_privacy, Preferences.getTudingUploadPrivacy(), new DialogInterface.OnClickListener() {
			@Override
			public void onClick(DialogInterface dialog, int which) {
				Logex.message("Recording", "You select "+which);
				Preferences.setTudingUploadPrivacy(which);
			}
		});
		builder.setPositiveButton(this.getString(R.string.OK_LABEL), new DialogInterface.OnClickListener(){
			@Override
			public void onClick(DialogInterface dialog, int which) {
			}
		});
		dl = builder.create();
		dl.show();
	}

	public static int which_val = 0; 
	private void showGPSConfigDialog() {
		Dialog dl = new Dialog(BigPlanet.this);
		AlertDialog.Builder builder = new AlertDialog.Builder(this);
		builder.setTitle(this.getString(R.string.GPS_CONFIG_TITLE));
		builder.setSingleChoiceItems(R.array.gpsintervalsel, Preferences.getGPSIntervalIndex(), new DialogInterface.OnClickListener() {
			@Override
			public void onClick(DialogInterface dialog, int which) {
				//Log.i("GPS", "You select "+which);
				which_val = which;
			}
		});
		
		builder.setPositiveButton(this.getString(R.string.OK_LABEL), new DialogInterface.OnClickListener(){
			@Override
			public void onClick(DialogInterface dialog, int which) {
				//dl.dismiss();
				Preferences.setGPSIntervalIndex(which_val);
				Preferences.setGPSIntervalValue(getGpsIntvalValue(which_val));
				//Log.i("GPS", "Interval value is "+Preferences.getGPSIntervalValue());
				// update GPS setting imm TODO: need test
				// also, need reset AP parms 
				// to avoid deadloc
				// to avoid go to sleep
				AutoPause.PauseGear = 0; 
				AutoPause.First_start_delay_counter = 1;
				AutoPause.First_start_flag = true;
				
				//Log.i("GPS", "You select "+which_val);
				currentTimeInterval = getGpsIntvalValue(which_val);
				Message msg = new Message(); 
				msg.what = 2;
				msg.arg1 = currentTimeInterval;
				updateLocationUpdateHandler.sendMessage(msg);
			}
        });
		dl = builder.create();
		dl.show();
		
	}
	
	private int getGpsIntvalValue(int which) {
		int intv = 4000;
		switch(which){
		case 0:
			intv = 4000;
			break;
		case 1:
			intv = 9000;
			break;
		case 2:
			intv = 18000;
			break;
		case 3:
			intv = 30000;
			break;
		case 4:
			intv = 120000;
			break;
		case 5:
			intv = 240000;
			break;
		case 6:
			intv = 600000;
			break;
		default:
			intv = 4000;
			break;
		}
		return intv;
	}
	/**
	 * Creates a radio button with the given parameters
	 * 
	 * @param label
	 * @param id
	 * @return
	 */
	private RadioButton buildRadioButton(String label, int id) {
		RadioButton btn = new RadioButton(this);
		btn.setText(label);
		btn.setId(id);
		return btn;
	}
	
	public static int getZoomLevelByLL(double latMax, double latMin, double lonMax, double lonMin ) {
		int zoomlevel = -2;
		int distance = (int)(1000*Math.max(latMax-latMin, lonMax-lonMin));
		while (true) {
			distance = distance >> 1;
			if(distance>=1) {
				zoomlevel++;
				//Log.i("ZOOM"," zoomlevel is "+zoomlevel+" distance is "+distance);
			}
			else break;
		}
		return (((zoomlevel+1)>17)?17:(zoomlevel+1));
	}

	/**
	 * Creates a dialog to select the mode (offline, online)
	 */
	/*
	private void selectNetworkMode() {
		final Dialog networkModeDialog;
		networkModeDialog = new Dialog(this);
		networkModeDialog.setCanceledOnTouchOutside(true);
		networkModeDialog.setCancelable(true);
		networkModeDialog.setTitle(R.string.NETWORK_MODE_MENU);
		
		final LinearLayout mainPanel = new LinearLayout(this);
		mainPanel.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,
				LayoutParams.FILL_PARENT));
		mainPanel.setOrientation(LinearLayout.VERTICAL);

		TextView mTextView = new TextView(this);
		mTextView.setText(R.string.SELECT_NETWORK_MODE_LABEL);
		mainPanel.addView(mTextView);
		
		RadioGroup modesRadioGroup = new RadioGroup(this);

		LinearLayout.LayoutParams layoutParams = new RadioGroup.LayoutParams(
				RadioGroup.LayoutParams.WRAP_CONTENT,
				RadioGroup.LayoutParams.WRAP_CONTENT);

		modesRadioGroup.addView(buildRadioButton(getResources().getString(
				(R.string.OFFLINE_MODE_LABEL)), 0), 0, layoutParams);

		modesRadioGroup.addView(buildRadioButton(getResources().getString(
				R.string.ONLINE_MODE_LABEL), 1), 0, layoutParams);

		boolean useNet = Preferences.getUseNet();
		int checked = 0;
		if (useNet) {
			checked = 1;
		}
		modesRadioGroup.check(checked);

		modesRadioGroup.setOnCheckedChangeListener(new OnCheckedChangeListener() {
					public void onCheckedChanged(RadioGroup group, int checkedId) {
						boolean useNet = checkedId == 1;
						mapControl.getPhysicalMap().getTileResolver()
								.setUseNet(useNet);
						Preferences.putUseNet(useNet);
						networkModeDialog.dismiss();
					}
				});

		mainPanel.addView(modesRadioGroup);
		networkModeDialog.setContentView(mainPanel);
		networkModeDialog.show();
	}

    */
	
	/**
	 * Creates a dialog to select the map source
	 */
	private void selectMapSource() {
		final Dialog mapSourceDialog;
		mapSourceDialog = new Dialog(this);
		mapSourceDialog.setCanceledOnTouchOutside(true);
		mapSourceDialog.setCancelable(true);
		mapSourceDialog.setTitle(R.string.SELECT_MAP_SOURCE_TITLE);

		ScrollView scrollPanel = new ScrollView(this);
		scrollPanel.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,
				LayoutParams.FILL_PARENT));

		final LinearLayout mainPanel = new LinearLayout(this);
		mainPanel.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,
				LayoutParams.FILL_PARENT));
		mainPanel.setOrientation(LinearLayout.VERTICAL);

		RadioGroup sourcesRadioGroup = new RadioGroup(this);

		LinearLayout.LayoutParams layoutParams = new RadioGroup.LayoutParams(
				RadioGroup.LayoutParams.WRAP_CONTENT,
				RadioGroup.LayoutParams.WRAP_CONTENT);

		for (Integer id : MapStrategyFactory.strategies.keySet()) {
			sourcesRadioGroup.addView(
					buildRadioButton(MapStrategyFactory.strategies.get(id)
							.getDescription(), id), 0, layoutParams);
		}

		sourcesRadioGroup.check(Preferences.getSourceId());

		sourcesRadioGroup.setOnCheckedChangeListener(new OnCheckedChangeListener() {
					public void onCheckedChanged(RadioGroup group, int checkedId) {
						Preferences.putSourceId(checkedId);
						mapControl.setMapSource(checkedId);
						mapSourceDialog.dismiss();
					}
				});

		mainPanel.addView(sourcesRadioGroup);
		scrollPanel.addView(mainPanel);
		mapSourceDialog.setContentView(scrollPanel);
		mapSourceDialog.show();
	}
	
	private void selectSQLiteDBFile() {
		final Dialog mSQLiteDBFileDialog;
		mSQLiteDBFileDialog = new Dialog(this);
		mSQLiteDBFileDialog.setCanceledOnTouchOutside(true);
		mSQLiteDBFileDialog.setCancelable(true);
		mSQLiteDBFileDialog.setTitle(R.string.SELECT_SQLite_DATABASE);

		ScrollView scrollPanel = new ScrollView(this);
		scrollPanel.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,
				LayoutParams.FILL_PARENT));

		final LinearLayout mainPanel = new LinearLayout(this);
		mainPanel.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,
				LayoutParams.FILL_PARENT));
		mainPanel.setOrientation(LinearLayout.VERTICAL);
		
		TextView mTextView = new TextView(this);
		String folderMessage = getString(R.string.SELECT_SQLite_Folder).replace("%s", SQLLocalStorage.MAP_PATH);
		mTextView.setText("  "+folderMessage+"\n");
		mainPanel.addView(mTextView);

		RadioGroup sqliteRadioGroup = new RadioGroup(this);

		LinearLayout.LayoutParams layoutParams = new RadioGroup.LayoutParams(
				RadioGroup.LayoutParams.WRAP_CONTENT,
				RadioGroup.LayoutParams.WRAP_CONTENT);
		
		final Map<Integer, String> sqliteMaps = new HashMap<Integer, String>();
		int mapsIndex = 1;
		// SQLLocalStorage.MAP_PATH = "/sdcard/RMaps/maps/";
		File mapsDBFolder = new File(SQLLocalStorage.MAP_PATH);
		if (mapsDBFolder.exists() && mapsDBFolder.isDirectory()) {
			String[] files = mapsDBFolder.list();
//			for (int i = 0; i < files.length; i++) {
//				Log.i("sqlitedb", files[i]);
//			}
			java.util.Arrays.sort(files);
			String[] filesSorted = new String[files.length];
			for (int i = 0; i < files.length; i++) {
				filesSorted[files.length-1-i] = files[i];
			}
			for (int i = 0; i < filesSorted.length; i++) {
				File sqliteFile = new File(mapsDBFolder, filesSorted[i]);
				if (sqliteFile.isFile()) {
					String strSQLiteName = sqliteFile.getName();
					if (strSQLiteName.endsWith(".sqlitedb")) {
						sqliteMaps.put(mapsIndex, strSQLiteName);
						mapsIndex++;
					} else {
						// skip...
					}
				}
			}
		}
		if (sqliteMaps.isEmpty()) {
			sqliteMaps.put(0, SQLLocalStorage.SQLITEDB);
			if (!mapsDBFolder.exists())
				mapsDBFolder.mkdirs();
		}
		mapsDBFolder = null;
		for (Integer key : sqliteMaps.keySet()) {
			String strSQLiteName = sqliteMaps.get(key);
			sqliteRadioGroup.addView(
					buildRadioButton(strSQLiteName, key), 0, layoutParams);
			if (strSQLiteName.equalsIgnoreCase(Preferences.getSQLiteName())) {
				sqliteRadioGroup.check(key);
			}
		}

		sqliteRadioGroup.setOnCheckedChangeListener(new OnCheckedChangeListener() {
					public void onCheckedChanged(RadioGroup group, int checkedId) {
						String strSQLiteName = sqliteMaps.get(checkedId);
						Preferences.putSQLiteName(strSQLiteName);
						// switch SQLite DB
						LocalStorageWrapper.switchLocalStorage();
						// update screen
						initializeMap();
						mSQLiteDBFileDialog.dismiss();
					}
				});

		mainPanel.addView(sqliteRadioGroup);
		scrollPanel.addView(mainPanel);
		mSQLiteDBFileDialog.setContentView(scrollPanel);
		mSQLiteDBFileDialog.show();
	}
	
	
	
	
	protected static String getTitle(String provider) {
		String strSQLiteName = Preferences.getSQLiteName();
		// remove ".sqlitedb"
		strSQLiteName = strSQLiteName.substring(0, strSQLiteName.lastIndexOf("."));
		// add more info
		if (provider != null) {
			provider = " @ " + provider;
		} else {
			provider = "";
		}
		int zoomLevel = PhysicMap.getZoomLevel();
		String zoom = String.valueOf(17-zoomLevel);
		String title = strSQLiteName + provider + " ["+ zoom + "]";
		return title;
	}
	
	private void setTitle(String title) {
		//TODO
		TextView subtitle = (TextView) findViewById(R.id.subtitle);
		subtitle.setText(title);
		
	}
	
	public void setActivityTitle(Activity activity) {
		String title = getTitle(BigPlanet.locationProvider);
		setTitle(title);
		// scale
		int zoomLevel = PhysicMap.getZoomLevel();
		if (scaledBitmapZoomLevel != zoomLevel) {
			scaledBitmapZoomLevel = zoomLevel;
			scaledBitmap = null;
		}
		StringBuffer zoom = new StringBuffer();
		zoom.append("scale").append(String.valueOf(17-zoomLevel));
		int imageID = activity.getResources().getIdentifier(zoom.toString(), "drawable", activity.getPackageName());
		if (imageID != 0) {
			if (density == 1) {
				scaleImageView.setImageResource(imageID);
			} else {
				try {
					zoomBitmap = BitmapFactory.decodeResource(activity.getResources(), imageID);
					if (zoomBitmap != null)
						scaleImageView.setImageBitmap(getScaledBitmap(zoomBitmap));
					else
						scaleImageView.setImageBitmap(null);
				} catch (Exception e) {
				}
			}
		}
	}
	
	private static Bitmap zoomBitmap = null;	
	private static Bitmap scaledBitmap = null;
	private static int scaledBitmapZoomLevel = -9;
	
	private static Bitmap getScaledBitmap(Bitmap bmp) {
		if (scaledBitmap == null || isMapMagnificationChanged) {
			int width = bmp.getWidth();
			int height = bmp.getHeight();
			double scale = 1/density*mapMagnification;
			int scaleWidth = (int)(width*scale);
			int scaleHeight = (int)(height*scale);
//			System.out.println(width+"*"+height);
//			System.out.println("scale = "+scale);
//			System.out.println(scaleWidth+"*"+scaleHeight);
			Bitmap scaledBmp = Bitmap.createBitmap(scaleWidth, scaleHeight, Bitmap.Config.ARGB_8888);
			Canvas canvas = new Canvas(scaledBmp);
			canvas.drawBitmap(bmp, null, new Rect(0, 0, scaleWidth, scaleHeight), null);
			scaledBitmap = scaledBmp;
		}
		return scaledBitmap;
	}

	public static int lastHeading = 0;

	/**
	 * Sets the pointer heading in degrees (will be drawn on next invalidate).
	 * 
	 * @return true if the visible heading changed (i.e. a redraw of pointer is
	 *         potentially necessary)
	 */
	protected static boolean setHeading(float heading) {
		int newhdg = Math.round(-heading / 360 * 18 + 180);
		while (newhdg < 0)
			newhdg += 18;
		while (newhdg > 17)
			newhdg -= 18;
		if (newhdg != lastHeading) {
			lastHeading = newhdg;
			return true;
		} else {
			return false;
		}
	}

}